Find what is actually burning CPU
CPU is pinned and pg_stat_activity is full of sessions — but most of them are waiting, not working. One column separates the backends on the CPU from the backends stuck behind something else, and it decides which fix is even relevant.
Problem
What you're actually looking at
The symptom as it shows up on a real server.
A host CPU graph tells you the box is saturated. It cannot tell you which backend is saturating it, and the obvious query — everything in pg_stat_activity that is not idle — mixes two completely different populations. A backend blocked on a lock is not using CPU at all, yet it looks identical in a naive listing.
Meridian Freight's checkout database sits at 98% CPU through a peak hour. The on-call engineer lists active sessions and sees forty of them. Thirty-six are queued behind a lock and consuming nothing; four are running an analytics query a dashboard is re-firing every few seconds. Reading the list without separating the two sends the fix in entirely the wrong direction.
In plain English
When PostgreSQL runs a query it is either doing work, or it is stopped waiting for something — a lock, the disk, or the client. Both of those show up as "active", which is why a list of busy sessions is so misleading during a CPU incident: most of the rows may be sessions that are stopped, not sessions that are burning the machine. There is one column that tells them apart. If a session has no wait reason recorded, nothing is holding it up, so it is genuinely running on a CPU right now. If it does have a wait reason, it is queued and consuming almost nothing. Split the list that way and the handful of real culprits is immediately obvious — and it also tells you whether you have a CPU problem at all, or a lock problem wearing a CPU costume.
Full runbook for this incident
- The full identify checklist — the exact signals that tell you it's this incident
- Every diagnostic query; PostgreSQL 18 output is attached only to the steps we captured
- The resolution path and the pitfalls that make it worse
- Mitigation steps to stop it recurring, plus a verify-you're-done query
More in this category
Other Observability runbooks
Neighbouring incidents that share the same diagnostic surface.
Connected
How this connects to the rest of the library
A live view of this page's real cross-references — what explains it, what fixes it, what to tune, and where to go next. Every link is an authored relationship, not a guess.
Fixes these errors