Skip to content

Instantly share code, notes, and snippets.

@possebon
Created April 21, 2026 18:05
Show Gist options
  • Select an option

  • Save possebon/021789714c0bcf6d3278bb8c50eef807 to your computer and use it in GitHub Desktop.

Select an option

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
-- 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