List PostgreSQL Wait Events with the pg_wait_events View
List PostgreSQL Wait Events with the pg_wait_events View
PostgreSQL 17 added pg_wait_events, a system view that enumerates every wait event the running server can record. Prior to 17 the same information lived only in the documentation — no in-database way to confirm a wait name or attach a description to it. The view is read-only, three columns wide, and exactly the lookup table a monitoring dashboard wants alongside pg_stat_activity.
Purpose and Overview
Wait-event monitoring is one of the highest-signal PostgreSQL observability surfaces. When a backend is not actively running CPU work, PostgreSQL records why it is paused in two columns of pg_stat_activity: wait_event_type (the broad category) and wait_event (the specific reason). Tracking which waits dominate over time tells a DBA whether a workload is bound by lock contention, I/O latency, internal lightweight-lock contention, or client-side stalls — a sharper diagnostic frame than CPU utilization or query duration alone.
pg_wait_events complements pg_stat_activity rather than replacing it. Where pg_stat_activity answers which session is waiting right now, pg_wait_events answers what this wait actually means. The view returns every wait the running server can post — categorized by type, named in the same string the activity view uses, and described in one human-readable sentence per row. The descriptions are maintained alongside the source code, which means they evolve as new wait events are instrumented and old ones are renamed.
The view is read-only and accessible to every role. There are no privilege grants to manage, no statistics to reset, and no runtime cost to query. Treat it as documentation that ships with the server — a version-pinned manifest of monitorable conditions for the running binary.
Sample Code
1-- Enumerate every wait event the server can record (PostgreSQL 17+)
2SELECT
3 type,
4 name,
5 description
6FROM pg_wait_events
7ORDER BY type, name;
Notes: Requires PostgreSQL 17 or later. The view is part of the base catalog and readable by any role. Returns roughly 350 rows on a stock 17.x server; the count grows with each release as new internal waits are exposed.
Code Breakdown
The query is intentionally minimal: a flat SELECT against a three-column catalog with a stable ordering. Each piece is worth understanding because the choices shape how the result is consumed downstream.
The pg_wait_events View
pg_wait_events is a system view introduced in PostgreSQL 17. It sits in the pg_catalog schema and is implicitly searchable from any role. One row exists per defined wait event in the running server binary; on a stock 17.x installation that is roughly 350 rows, with the count growing each major release. The view is a static catalog — the rows do not change between queries on the same server version, so the same result will return millisecond after millisecond.
Columns Returned
Three columns are exposed. type (text) is the broad wait event category — LWLock, Lock, IO, IPC, Client, Timeout, BufferPin, Extension, or Activity. name (text) is the specific wait inside the category, matching exactly the string pg_stat_activity.wait_event uses when a backend is in that wait. description (text) is the one-line human-readable explanation maintained alongside the C source code.
Ordering and Reading Strategy
ORDER BY type, name groups the result by category, which is the natural way to read the catalog because categories map directly to the diagnostic frames a DBA reaches for. Reading all IO rows in one block surfaces the disk-related waits as a coherent set; reading all Lock rows surfaces the relational-lock surface. The alphabetical secondary sort within each category matches the order the PostgreSQL documentation uses.
Key Wait Event Categories
Lock and LWLock
Lock waits represent heavyweight locks held between transactions — the locking layer SQL programmers reason about directly via LOCK TABLE, SELECT ... FOR UPDATE, and implicit row-level locks from UPDATE/DELETE. The specific wait names (relation, tuple, transactionid, virtualxid) point to the lock granularity. LWLock waits are internal lightweight locks PostgreSQL uses to coordinate access to shared-memory data structures. Names like BufferMapping, LockManager, and WALWrite identify the contention point inside the server.
IO
IO waits cover disk reads and writes — DataFileRead for a heap or index page fetch, WALWrite and WALSync for WAL flushing, DataFileExtend for table growth, and RelationMapRead for the relation-mapping bootstrap. A workload dominated by IO waits is usually addressed by increasing shared_buffers, improving disk throughput, or both — though WALSync specifically points at the WAL volume's commit latency rather than data-file throughput.
IPC and Client
IPC waits cover inter-process communication: backends waiting on the WAL writer to flush, on the checkpointer to finish, on a parallel worker to return results, or on a logical replication apply worker. Client waits cover the application side — ClientRead (waiting for the next query) is the most common and usually points to a connection-pool or application-side issue, not a server problem.
Timeout, BufferPin, and Activity
Timeout waits are explicit sleeps — PgSleep, RecoveryRetrieveRetryInterval, VacuumDelay. BufferPin is a short-term wait while a buffer is pinned by another backend; persistent BufferPin waits are rare. Activity waits are the long-idle states of background workers — AutovacuumLauncherMain, CheckpointerMain, WalWriterMain, LogicalLauncherMain. These are normal, not contention; suppress them on operational dashboards.
Extension
Extension waits are defined by loaded extensions. Common examples: pg_stat_statements waits, logical replication output plugin waits, partition routing waits from third-party extensions. These rows only appear in pg_wait_events when the extension is loaded into the server, so the catalog reflects what the running configuration can actually report.
Practical Applications
The view's value compounds when joined to live activity or compared across versions.
Live-Waits Dashboard Lookup
The canonical use case is joining pg_stat_activity to pg_wait_events so a dashboard renders both the wait name and its description without hard-coding a lookup table. This keeps the dashboard accurate as the catalog evolves between minor releases.
1SELECT
2 sa.pid,
3 sa.datname,
4 sa.usename,
5 sa.wait_event_type,
6 sa.wait_event,
7 we.description,
8 LEFT(sa.query, 100) AS query_snippet
9FROM pg_stat_activity sa
10LEFT JOIN pg_wait_events we
11 ON we.type = sa.wait_event_type
12 AND we.name = sa.wait_event
13WHERE sa.wait_event IS NOT NULL
14ORDER BY sa.wait_event_type, sa.wait_event;
Activity-Class Suppression for Operational Monitoring
The Activity class floods live-wait dashboards with idle background-worker waits — interesting once to confirm those processes are alive, useless for the rest of the day. Excluding the category removes the noise without losing visibility into the production-relevant IO, Lock, and LWLock waits that signal actual work being held up.
1SELECT
2 sa.pid,
3 sa.wait_event_type,
4 sa.wait_event,
5 we.description,
6 LEFT(sa.query, 80) AS query_snippet
7FROM pg_stat_activity sa
8LEFT JOIN pg_wait_events we
9 ON we.type = sa.wait_event_type
10 AND we.name = sa.wait_event
11WHERE sa.wait_event IS NOT NULL
12 AND sa.wait_event_type <> 'Activity';
Cross-Version Upgrade Diff
Capturing the catalog on the old cluster, capturing again on the new one, and EXCEPT-diffing the two surfaces every wait the upgrade added. PostgreSQL 17 introduced several new entries as wait-event instrumentation was extended; later major versions add more as new I/O paths land. The diff turns a sometimes-vague release note into a concrete list of new monitoring points to surface in dashboards.
1SELECT type, name FROM pg_wait_events
2EXCEPT
3SELECT type, name FROM old_cluster_wait_events_snapshot
4ORDER BY type, name;
Extension Wait Event Debugging
When a loaded extension introduces new waits, those rows appear in pg_wait_events under type = 'Extension'. Querying the catalog filtered to WHERE type = 'Extension' after installing or upgrading an extension confirms which extension-defined waits the server can now report — which makes troubleshooting that extension's idle states tractable instead of guesswork.
Version Compatibility
pg_wait_events was added in PostgreSQL 17 and is not available on earlier major versions. On PostgreSQL 16 and earlier the same information is documented in the manual under "Wait Event Types" but has no in-database surface — the workaround is to scrape the documentation HTML into a static lookup table and join it against pg_stat_activity until you can upgrade. Plan to retire that static table once the cluster moves to 17 or later.
The source-of-truth file that powers pg_wait_events is src/backend/utils/activity/wait_event_names.txt in the PostgreSQL source repository. The build process generates the C enum, the documentation tables, and the runtime rows from that single file. Reading the file in your installed major version's branch shows exactly which waits exist and how they are described — useful when a description in the running view seems too terse to interpret on its own.
PostgreSQL 18 adds further wait events as asynchronous I/O work landed, so a 17-to-18 upgrade is one of the cases where the EXCEPT diff in the practical applications section yields the most material change.
Best Practices
- Treat the view as documentation that ships with the server — version-pinned, doesn't change at runtime, no statistics to reset
- Join live wait activity rather than hard-coding a lookup — the joined query stays accurate across minor releases without code changes
- Suppress the Activity class on operational dashboards — long-idle background-worker waits are normal and drown out the real signal
- Snapshot the catalog before every major upgrade —
EXCEPTagainst the new-version catalog reveals every wait the upgrade instrumented - Read
wait_event_names.txtfor the underlying definitions — when a one-line description doesn't disambiguate, the source file's tab-separated rows include compiler-level context - No privilege grants needed —
pg_monitor(PostgreSQL 10+) already coverspg_stat_activity;pg_wait_eventsis world-readable on top of that
References
- PostgreSQL: pg_wait_events — official catalog reference for the view introduced in PostgreSQL 17
- PostgreSQL: Wait Event Types — manual reference enumerating every wait event and its category
- PostgreSQL: pg_stat_activity — the live activity view that
pg_wait_eventscomplements - postgres/postgres: wait_event_names.txt — source-of-truth file that generates the catalog, documentation, and runtime rows
Posts in this series
- How Many Connections Can Your PostgreSQL Database Handle?
- PostgreSQL Backend Connections via pg_stat_database
- Identifying Blocking PostgreSQL Queries using pg_stat_activity
- List PostgreSQL Databases by Size with Access Check
- Assess PostgreSQL Database Sizes Quickly and Easily
- Unveiling Your PostgreSQL Server - A Diagnostic Powerhouse
- Keep Your PostgreSQL Database Clean, Identify Idle Connections
- Query the PostgreSQL Configuration
- PostgreSQL Recovery Monitoring: Essential SQL Insights
- Restart All PostgreSQL Sequences with ALTER SEQUENCE
- Monitor Running Queries in PostgreSQL using pg_stat_activity
- Monitor PostgreSQL Active Sessions with pg_stat_activity
- PostgreSQL Error Handling Settings via pg_settings
- PostgreSQL File Location Settings Query via pg_settings
- PostgreSQL Lock Management Settings via pg_settings
- PostgreSQL Logging Configuration Query via pg_settings
- Monitor PostgreSQL Memory Settings with pg_settings
- PostgreSQL Table Row Count Estimates with SQL
- List PostgreSQL Tables by Size with SQL
- PostgreSQL WAL Settings Query Guide
- PostgreSQL SSL Settings Query Guide
- PostgreSQL Statistics Settings Query Guide
- PostgreSQL Resource Settings Query Guide
- PostgreSQL Replication Settings Query Guide
- PostgreSQL Query Planning Settings Query Guide
- PostgreSQL Preset Options Settings Query Guide
- PostgreSQL Miscellaneous Settings Query Guide
- Count PostgreSQL Sessions by State with SQL
- Kill Idle PostgreSQL Sessions with SQL
- Grant SELECT on All Tables in PostgreSQL
- Identify Insert-Only Tables in PostgreSQL
- Detect Soft Delete Patterns in PostgreSQL
- List PostgreSQL Object Comments with SQL
- List Foreign Key Constraints in PostgreSQL
- List PostgreSQL Enum Types and Their Values with SQL
- List All Views in a PostgreSQL Database with SQL
- Find PostgreSQL Tables Without a Primary Key
- List PostgreSQL Partitioned Tables with SQL
- List All Schemas in Your PostgreSQL Database
- PostgreSQL Database Statistics with pg_stat_database
- List PostgreSQL Roles and Their Privileges
- Scrubbing Email PII in PostgreSQL for GDPR Compliance
- List Installed Extensions in PostgreSQL
- List Collations in Your PostgreSQL Database
- PostgreSQL Replica Identity for Logical Replication
- Monitor PostgreSQL Vacuum Progress with pg_stat_progress_vacuum
- Monitor PostgreSQL Wait Events Using pg_stat_activity
- Monitor PostgreSQL Replication Lag with pg_stat_replication
- List PostgreSQL Wait Events with the pg_wait_events View