Created
April 21, 2026 18:05
-
-
Save possebon/021789714c0bcf6d3278bb8c50eef807 to your computer and use it in GitHub Desktop.
pg_wait_sampling quickstart: the ASH of PostgreSQL — install, cumulative profile, time-ordered history
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| -- pg_wait_sampling quickstart | |
| -- The Postgres equivalent of Oracle ASH or SQL Server wait stats. | |
| -- Samples wait events from shared memory at sub-millisecond intervals. | |
| -- | |
| -- Docs: https://github.com/postgrespro/pg_wait_sampling | |
| -- Works on PostgreSQL 11+. | |
| -- Blog post: https://www.linkedin.com/in/fernando-possebon/ | |
| -- 1. Add to shared_preload_libraries (requires a Postgres restart). | |
| -- Edit postgresql.conf, or use ALTER SYSTEM: | |
| ALTER SYSTEM SET shared_preload_libraries = 'pg_wait_sampling'; | |
| -- then restart the server. | |
| -- 2. Create the extension (per database, one time). | |
| CREATE EXTENSION IF NOT EXISTS pg_wait_sampling; | |
| -- 3. Cumulative wait-event profile — the "what's this database bottlenecked on" query. | |
| SELECT event_type, | |
| event, | |
| count | |
| FROM pg_wait_sampling_profile | |
| ORDER BY count DESC | |
| LIMIT 20; | |
| -- 4. Top wait events by queryid — joins to pg_stat_statements for the SQL text. | |
| SELECT p.event_type, | |
| p.event, | |
| p.count, | |
| LEFT(s.query, 200) AS query_preview | |
| FROM pg_wait_sampling_profile p | |
| LEFT JOIN pg_stat_statements s ON s.queryid = p.queryid | |
| ORDER BY p.count DESC | |
| LIMIT 25; | |
| -- 5. Recent wait-event history — the "what was it doing at 14:03" query. | |
| SELECT ts, pid, event_type, event, queryid | |
| FROM pg_wait_sampling_history | |
| WHERE ts > now() - interval '5 minutes' | |
| ORDER BY ts DESC | |
| LIMIT 500; | |
| -- 6. Optional: crank retention and sampling resolution before you need them. | |
| -- Defaults: history_size = 5000 samples, history_period = 10 ms. | |
| ALTER SYSTEM SET pg_wait_sampling.history_size = 50000; | |
| ALTER SYSTEM SET pg_wait_sampling.history_period = 10; -- ms, keep at 10 unless benchmarked | |
| SELECT pg_reload_conf(); | |
| -- Three things worth knowing the first week you run it: | |
| -- | |
| -- queryid in pg_wait_sampling aligns with pg_stat_statements.queryid. | |
| -- That join turns wait-events into "this specific query is stuck on this specific wait." | |
| -- | |
| -- event_type = 'LWLock' on a modern PG is usually buffer-mapping or WAL contention. | |
| -- Look at shared_buffers and wal_buffers before rewriting SQL. | |
| -- | |
| -- Sampling is cheap but not free. On 1000+ tps, leave defaults alone. | |
| -- Crank history_size only during a specific investigation. | |
| -- | |
| -- NOT available on AWS RDS or GCP CloudSQL — neither platform lets you add | |
| -- shared_preload_libraries. If you run managed Postgres and performance matters, | |
| -- that constraint is one more line on the self-managed-is-cheaper calculation. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment