PostgreSQLXVI · Observability, Logging and AlertingObservability
Wait events: what is this backend actually waiting on?
What you'll learn
- Read wait_event_type and wait_event and know what each class means
- Sample wait events to build a profile rather than a snapshot
- Distinguish contention from I/O from genuine work
- Corroborate a diagnosis with a second instrument
Prerequisites
Verified against PostgreSQL 18.x · PostgreSQL (comparison targets) 17.11, 16.15 · PostgreSQL (support calendar) 18, 17, 16, 15, 14 supported · pgBackRest 2.59.1 · PgBouncer 1.25.2 · Patroni 4.1.5 · Ubuntu (host baseline) 26.04 LTS · 2026-08-27
state = 'active' means a backend is executing a statement. It does not
mean the backend is doing anything. The wait event tells you which.
The classes
pg_stat_activity.wait_event_type puts every wait into a class, and the
class alone narrows the problem enormously.
| Type | Means | Typically |
|---|---|---|
Lock | Waiting for a heavyweight lock | Application contention. Part IX |
LWLock | Waiting for an internal shared-memory lock | Internal contention |
IO | Waiting for storage | Slow disk, or too little cache |
BufferPin | Waiting to pin a buffer another backend holds | Rare |
Client | Waiting for the client | Not the database’s problem |
IPC | Waiting for another PostgreSQL process | Parallel query, sync rep |
Timeout | Sleeping deliberately | Usually normal |
Extension | An extension’s own wait | Depends |
NULL | Not waiting. Actually running | On CPU |
Sampling, not snapshotting
A single query against pg_stat_activity catches whatever happened to be
true at that instant. One sample is an anecdote. A profile needs many.
$ SELECT wait_event_type, wait_event, count(*)
FROM pg_stat_activity
WHERE backend_type='client backend' AND state='active'
GROUP BY 1,2 ORDER BY 3 DESC; 6 IO|WalSync|1
5 (running)|-|1
3 Lock|tuple|3
3 LWLock|WALWrite|2
2 Lock|tuple|2
2 Lock|transactionid|10
2 LWLock|WALWrite|3
1 Lock|transactionid|11
1 Lock|transactionid|9
1 Lock|transactionid|8
1 Lock|transactionid|6Up to eleven of sixteen backends waiting on Lock/transactionid
simultaneously.
Lock/transactionid means: this backend wants a row that another
transaction has modified and not yet committed, so it is waiting for
that transaction to finish. It is row-level contention, and it is
exactly Part IX’s subject seen from the waiting side.
The rest of the profile is consistent: Lock/tuple is the queue for the
same rows, LWLock/WALWrite and IO/WalSync are the WAL write path
under load.
-- a poor man's sampler: run every second, accumulate, and look at the shape
SELECT now(), wait_event_type, wait_event, count(*)
FROM pg_stat_activity
WHERE backend_type = 'client backend' AND state = 'active'
GROUP BY 1, 2, 3;
Reading the common ones
| Event | Means | Look at |
|---|---|---|
Lock/transactionid | Waiting for another transaction’s row | The blocking transaction. Lesson IX-03 |
Lock/relation | Waiting for a table lock | DDL, or LOCK TABLE. Lesson IX-05 |
Lock/tuple | Queued for a row lock | Same root cause |
LWLock/WALWrite | Waiting to write WAL | Commit rate, wal_buffers |
LWLock/BufferContent | Contention on one buffer | A very hot page |
IO/DataFileRead | Reading a table or index | shared_buffers, storage |
IO/WalSync | Waiting for WAL fsync | Storage commit latency. Lesson VI-01 |
Client/ClientRead | Waiting for the application | The application |
IPC/SyncRep | Waiting for a synchronous standby | Lesson XIV-05 |
Timeout/VacuumDelay | Autovacuum’s cost delay | Lesson VIII-03 |
What to take from this
state = 'active'says executing, not working. The wait event says which.- The class narrows it enormously:
Lockis application contention,IOis storage,Clientis not your problem. Client/ClientReadwithidle in transactionis lesson VII-05’s problem in one row.- Sample repeatedly; one reading is an anecdote.
- Measured: 11 of 16 backends on
Lock/transactionid, corroboratingpg_stat_statementsfrom the opposite direction. - PostgreSQL keeps no wait history by design. Sample for shape, use
log_lock_waitsfor individual events.
Cross-course references
- Linux for Production Sysadmins — Part XXXVIII (Linux performance fundamentals) and Part XLIII (eBPF) cover confirming a wait event against what the kernel says the process is doing.
- Observability for Production Sysadmins — Part LIX (Database observability) covers sampling wait events into a series, since a single sample is an anecdote.
Quiz
Knowledge check · 6 questions
Q1. A backend shows state 'idle in transaction' with wait_event Client/ClientRead. What is happening?
Q2. Sampling wait events at 1 Hz on a system whose average wait is 5 ms. What is that good for and not good for?
Q3. Why does PostgreSQL keep no history of wait events?
Q4. Which wait events point at application-level lock contention? Select all that apply.
Q5. A backend with wait_event NULL and state active is running on CPU rather than waiting.
Q6. The wait-event profile showed eleven of sixteen backends on Lock/transactionid. How would you corroborate that diagnosis, and why bother?
Passing score: 75%. Answers are checked in this browser.