CPU, memory, disk and network all look healthy, and the service will not go faster. What are you looking for?
Show the full answer Hide the answer
What is being tested
Whether you know that physical resources are the monitored ones and logical resources are the ones that actually bind.
What you are looking for
A logical resource limit. Something that is exhausted and is not on any infrastructure dashboard:
- A connection pool — application to database, application to a downstream, a pooler's own pool. Threads waiting for a connection consume no CPU and show as idle.
- A thread or worker pool. All workers blocked, new requests queueing, CPU at 4%.
- A lock. A distributed lock, a hot row, a global lock inside a shared library, a synchronised cache. Everything serialises and nothing looks busy.
- A semaphore or concurrency limit set years ago and never revisited.
- A third-party or cloud service quota — API rate limit, provisioned throughput, per-account service limit.
- A single-threaded component in the path: a coordinator, a sequencer, a leader that all work passes through.
- File descriptors or ephemeral ports, which produce intermittent connection failures rather than a clean saturation signal.
- A downstream dependency's capacity, which is a bottleneck outside your system and is frequently treated as an internal problem.
The diagnostic distinction
Utilisation is not saturation. Utilisation says how busy something is; saturation says how much work is waiting. Every one of the resources above shows as low utilisation while being completely saturated, because waiting consumes no measurable resource.
So look for queue depth and wait time, not for busyness:
- Connection pool wait time and pool exhaustion counts.
- Thread pool queue depth.
- Lock wait time.
- Time spent blocked, from a wall-clock profile.
The fastest route to the answer
Trace a slow request. Wall-clock time in a trace points directly at the waiting, whereas resource metrics require you to infer it. A span showing 800 ms of "acquiring connection" ends the investigation immediately.
Failing that, a wall-clock or off-CPU profile, which attributes time to where threads are blocked rather than where they are executing.
Why this class is so often missed
Infrastructure monitoring is provided by default; logical resource monitoring must be deliberately instrumented. Most teams have CPU graphs and no connection-pool-wait graph, so the bottleneck they have is the one they cannot see.
The remedy is to instrument every pool in the system with saturation and wait metrics, and to put them on the incident dashboard. It is a small amount of work and it converts the most common hard-to-diagnose incident into an obvious one.
What a strong answer adds
That relieving one of these moves the bottleneck rather than removing it, so each round of work must re-identify the constraint — and that the bottleneck is frequently not where the symptom is: a slow endpoint is often blocked on a pool exhausted by an entirely unrelated dependency.