How to Inspect Waiting and Granted Locks in PostgreSQL

PostgreSQL · Lock inspection · DBA-019

How to Inspect Waiting and Granted Locks in PostgreSQL

Inspect PostgreSQL lock modes, identify requests that are waiting, and understand which lock details matter during an incident.

The short answer

Query pg_locks and join it to pg_stat_activity to see granted locks and pending requests. Focus first on rows where granted = false, then use pg_blocking_pids() when you need an authoritative blocker relationship rather than attempting to infer it from lock rows alone.

The SQL diagnostic

Run this read-only query against the PostgreSQL environment you are investigating. Interpret the results in the context of your workload and privileges.

PostgreSQL · lock requests and grants
WITH current_db AS (
    SELECT oid AS db_oid
    FROM pg_database
    WHERE datname = current_database()
)
SELECT
    l.pid,
    a.usename,
    a.state,
    l.locktype,
    l.mode,
    l.granted,
    l.waitstart,
    CASE WHEN c.oid IS NOT NULL
         THEN n.nspname || '.' || c.relname END
         AS relation_name,
    l.transactionid
FROM pg_locks AS l
CROSS JOIN current_db AS d
LEFT JOIN pg_stat_activity AS a ON a.pid = l.pid
LEFT JOIN pg_class AS c
    ON c.oid = l.relation AND l.database = d.db_oid
LEFT JOIN pg_namespace AS n ON n.oid = c.relnamespace
WHERE l.pid IS DISTINCT FROM pg_backend_pid()
  AND (l.database = d.db_oid
       OR l.database IS NULL OR l.database = 0)
ORDER BY l.granted ASC, l.waitstart NULLS LAST, l.pid
LIMIT 200;

Adapted from DBA-019. Uses LEFT JOIN to retain lock rows without a live backend and limits relation-name lookup to the current database.

How to read the results

grantedfalse indicates a lock request that has not yet been granted; these appear first.
modeRequested or held lock mode, such as AccessShareLock or AccessExclusiveLock.
waitstartWhen the backend began waiting for this lock, if applicable; it may be NULL immediately after waiting starts.
relation_nameSchema and relation when a relation OID can be resolved in the current database. Blank does not mean “no lock.”

PostgreSQL tracks relation locks, transaction-ID locks and other lock types. Many row-level locks are not represented as an obvious table-and-row entry in pg_locks. A waiting writer can instead show a wait on a transaction ID.

This is a broad inspection report capped at 200 rows. It is not a complete lock-graph calculation, and it is not a list of which sessions should be terminated.

What to check next

  1. Look for ungranted requests. Inspect the lock mode, wait start and session state for rows with granted = false.
  2. Watch DDL carefully. Strong relation locks requested by schema changes can create queues behind long transactions.
  3. Resolve who blocks whom separately. Use DBA-018 and pg_blocking_pids() for blocker relationships; joining locks against locks is easy to get wrong.
  4. Use safe timeouts for planned migrations. Consider a deliberate lock_timeout as part of a change plan, not as an afterthought during an outage.

Related DBA tasks

Continue the investigation with these recipes from SQL Recipes for DBAs: DBA-016 (long-running transactions), DBA-017 (cancel or terminate when justified), and DBA-018 (blocking-session chains).

Continue learning · Baldwin Apps

More practical solutions in SQL Recipes for DBAs

Find engine-specific recipes for sessions, locks, performance, permissions, backups, maintenance, and troubleshooting. Explore the book and its existing companion materials.

Published

Leave a comment

Your email address will not be published. Required fields are marked *