Lock Contention
System views: pg_locks, pg_stat_activity, pg_blocking_pids
Overview
Lock contention occurs when one session holds a lock that another session needs before it can proceed. In Postgres, the most common form is a long-running query holding a row-level or table-level lock while DDL (ALTER TABLE, CREATE INDEX without CONCURRENTLY, VACUUM FULL) tries to acquire an AccessExclusiveLock. The DDL blocks, and all subsequent queries that need even a weaker lock queue behind it—creating a thundering-herd pile-up.
insightral uses pg_blocking_pids() to detect lock wait chains and reports the root blocker, the wait duration, and the lock mode. Common fixes are: run DDL with CONCURRENTLY, set lock_timeout on DDL statements, and avoid long transactions that accumulate row locks.
Detection SQL
Run this query with a read-only role. No writes, no locks.
-- Sessions waiting on locks and their blockers
SELECT
blocked.pid AS blocked_pid,
blocked.usename AS blocked_user,
blocked.application_name,
left(blocked.query, 80) AS blocked_query,
now() - blocked.query_start AS wait_duration,
blocking.pid AS blocking_pid,
blocking.usename AS blocking_user,
left(blocking.query, 80) AS blocking_query,
locks.locktype,
locks.relation::regclass AS locked_relation,
locks.mode
FROM pg_stat_activity blocked
JOIN pg_stat_activity blocking
ON blocking.pid = ANY(pg_blocking_pids(blocked.pid))
JOIN pg_locks locks
ON locks.pid = blocking.pid
AND NOT locks.granted
WHERE blocked.wait_event_type = 'Lock'
ORDER BY wait_duration DESC;Fix SQL
Replace placeholders in <angle brackets> with values from the detection output before running.
-- Terminate the blocking session (confirm with application team first)
SELECT pg_terminate_backend(<blocking_pid>);
-- For DDL lock contention, use lock_timeout to fail fast
SET lock_timeout = '2s';
ALTER TABLE <table_name> ADD COLUMN <col> <type>; -- fails fast if lockedWorked example
Deployment script ran ALTER TABLE orders ADD COLUMN metadata JSONB. A long-running report query held a ShareLock on orders (27 seconds). The ALTER queued, and all 140 subsequent app queries stacked behind it. Site appeared frozen for 30 seconds. Fix: set lock_timeout = '3s' on the migration connection and retry — this pattern fails fast rather than blocking all app traffic.
Common false positives
- Expected locking during intentional schema migrations in a maintenance window — normal behaviour.
- Advisory locks taken by application-level distributed mutexes (pg_try_advisory_lock) appear as lock contention but are deliberate.
Check for lock contention across your whole estate.
Insightral runs this rule — and 98 more — continuously inside your private cloud, with read-only credentials and nothing leaving your network.
Request early accessLast updated: 2026-05-13 · View all published rules
