Skip to content

This is the multi-page printable view of this section. .

Return to the regular view of this page.

Lock waits

Heavyweight locks whose owner and blocking chain can usually be identified from SQL.

Lock is the most directly actionable class: a backend asked the lock manager for a heavyweight lock and an incompatible holder has not released it. Unlike LWLocks, these waits usually have rows in pg_locks and a useful result from pg_blocking_pids().

Read the blocking graph, not the victim count

Twenty waiters can all be symptoms of one idle transaction. Start at the head of the graph, then decide whether the holder is doing useful work, abandoned, or participating in an expected DDL/deploy window.

  • Normal: sub-second lock handoff during ordinary writes or planned DDL.
  • Watch: any user-facing waiter persists beyond its latency objective, or at least 10% of active sessions wait on the same root blocker.
  • Urgent: the root holder is idle in transaction, the chain keeps growing, critical DDL blocks traffic, or deadlocks begin to rise.

Events to recognize

Event Usually means
relation Table/index-level lock conflict, often DDL versus DML
transactionid Waiting for another transaction’s outcome, commonly row updates
tuple Competing tuple locks or a long row-lock queue
extend Sessions serialize while extending one relation
virtualxid DDL waits for transactions that might still use an object
advisory Application-defined advisory-lock coordination

Blocker query

SELECT w.pid AS waiting_pid, b.pid AS blocking_pid,
       w.wait_event, now() - w.query_start AS waiting_for,
       now() - b.xact_start AS blocker_xact_age,
       b.state AS blocker_state,
       left(w.query, 100) AS waiting_query,
       left(b.query, 100) AS blocking_query
FROM pg_stat_activity AS w
CROSS JOIN LATERAL unnest(pg_blocking_pids(w.pid)) AS x(pid)
JOIN pg_stat_activity AS b ON b.pid = x.pid
ORDER BY waiting_for DESC;

Common misreads

  • A session listed as a blocker is not automatically safe to terminate; it may be the only writer preserving an invariant.
  • transactionid does not mean transaction-ID exhaustion.
  • relation does not identify the relation by itself; join pg_locks.relation to pg_class.
  • Increasing lock_timeout changes how long victims wait; it does not remove the blocker.

1 - Lock: advisory

Waiting to acquire an advisory user lock
PostgreSQL wait event dossier
ClassLock Eventadvisory VersionsPG 13-18 Evidence3 source location(s)

Official description

Waiting to acquire an advisory user lock

PG 13 PG 14 PG 15 PG 16 PG 17 PG 18

Trigger mechanism

PG_WAIT_LOCK at src/backend/storage/lmgr/proc.c:1487 is the grep-verified reporting path. The public identity/resource is mapped at src/backend/utils/activity/wait_event_names.txt:428. The instrumented operation is: Waiting to acquire an advisory user lock. The lock manager could not grant the advisory heavyweight lock immediately. ProcSleep reports the lock wait and parks the backend on the lock’s wait queue until owners release or the request is cancelled.

Normal or trouble?

  • Normal: Sub-second handoff during ordinary writes or planned DDL can be normal.
  • Investigate: Investigate once a user-facing wait breaches its latency objective, a blocking chain grows, or the root holder is idle in transaction.

Diagnostic SQL

Sessions waiting on Lock/advisory
SELECT pid, backend_type, usename, datname, application_name,
       state, now() - query_start AS query_age,
       now() - xact_start AS xact_age,
       wait_event_type, wait_event,
       pg_blocking_pids(pid) AS blocking_pids,
       left(query, 160) AS query
FROM pg_stat_activity
WHERE wait_event_type = 'Lock'
  AND wait_event = 'advisory'
ORDER BY query_age DESC NULLS LAST;
Current Lock cohort
SELECT wait_event, count(*) AS waiting_sessions,
       count(*) FILTER (WHERE state = 'active') AS active_waiters,
       max(now() - query_start) AS oldest_query
FROM pg_stat_activity
WHERE wait_event_type = 'Lock'
GROUP BY wait_event
ORDER BY waiting_sessions DESC, wait_event;
Lock and relation context for these sessions
SELECT a.pid, l.locktype, l.mode, l.granted, l.fastpath,
       d.datname, n.nspname, c.relname,
       l.page, l.tuple, l.virtualxid, l.transactionid,
       l.classid, l.objid, l.objsubid
FROM pg_stat_activity AS a
LEFT JOIN pg_locks AS l ON l.pid = a.pid
LEFT JOIN pg_database AS d ON d.oid = l.database
LEFT JOIN pg_class AS c
  ON c.oid = l.relation
 AND l.database = (
       SELECT oid FROM pg_database WHERE datname = current_database()
     )
LEFT JOIN pg_namespace AS n ON n.oid = c.relnamespace
WHERE a.wait_event_type = 'Lock'
  AND a.wait_event = 'advisory'
ORDER BY a.pid, l.granted, l.locktype, l.mode;

Response

  1. Build the pg_blocking_pids graph to its root.
  2. Inspect the root holder’s state, transaction age, and business purpose.
  3. Choose cancellation, timeout, or workload sequencing only after identifying the safest root action.

Source evidence

2 - Lock: applytransaction

Waiting to acquire a lock on a remote transaction being applied by a logical replication subscriber
PostgreSQL wait event dossier
ClassLock Eventapplytransaction VersionsPG 16-18 Evidence3 source location(s)

Official description

Waiting to acquire a lock on a remote transaction being applied by a logical replication subscriber

PG 13 PG 14 PG 15 PG 16 PG 17 PG 18

Trigger mechanism

PG_WAIT_LOCK at src/backend/storage/lmgr/proc.c:1487 is the grep-verified reporting path. The public identity/resource is mapped at src/backend/utils/activity/wait_event_names.txt:429. The instrumented operation is: Waiting to acquire a lock on a remote transaction being applied by a logical replication subscriber. The lock manager could not grant the applytransaction heavyweight lock immediately. ProcSleep reports the lock wait and parks the backend on the lock’s wait queue until owners release or the request is cancelled.

Normal or trouble?

  • Normal: Sub-second handoff during ordinary writes or planned DDL can be normal.
  • Investigate: Investigate once a user-facing wait breaches its latency objective, a blocking chain grows, or the root holder is idle in transaction.

Diagnostic SQL

Sessions waiting on Lock/applytransaction
SELECT pid, backend_type, usename, datname, application_name,
       state, now() - query_start AS query_age,
       now() - xact_start AS xact_age,
       wait_event_type, wait_event,
       pg_blocking_pids(pid) AS blocking_pids,
       left(query, 160) AS query
FROM pg_stat_activity
WHERE wait_event_type = 'Lock'
  AND wait_event = 'applytransaction'
ORDER BY query_age DESC NULLS LAST;
Current Lock cohort
SELECT wait_event, count(*) AS waiting_sessions,
       count(*) FILTER (WHERE state = 'active') AS active_waiters,
       max(now() - query_start) AS oldest_query
FROM pg_stat_activity
WHERE wait_event_type = 'Lock'
GROUP BY wait_event
ORDER BY waiting_sessions DESC, wait_event;
Lock and relation context for these sessions
SELECT a.pid, l.locktype, l.mode, l.granted, l.fastpath,
       d.datname, n.nspname, c.relname,
       l.page, l.tuple, l.virtualxid, l.transactionid,
       l.classid, l.objid, l.objsubid
FROM pg_stat_activity AS a
LEFT JOIN pg_locks AS l ON l.pid = a.pid
LEFT JOIN pg_database AS d ON d.oid = l.database
LEFT JOIN pg_class AS c
  ON c.oid = l.relation
 AND l.database = (
       SELECT oid FROM pg_database WHERE datname = current_database()
     )
LEFT JOIN pg_namespace AS n ON n.oid = c.relnamespace
WHERE a.wait_event_type = 'Lock'
  AND a.wait_event = 'applytransaction'
ORDER BY a.pid, l.granted, l.locktype, l.mode;

Response

  1. Build the pg_blocking_pids graph to its root.
  2. Inspect the root holder’s state, transaction age, and business purpose.
  3. Choose cancellation, timeout, or workload sequencing only after identifying the safest root action.

Source evidence

3 - Lock: extend

Waiting to extend a relation
PostgreSQL wait event dossier
ClassLock Eventextend VersionsPG 13-18 Evidence3 source location(s)

Official description

Waiting to extend a relation

PG 13 PG 14 PG 15 PG 16 PG 17 PG 18

Trigger mechanism

PG_WAIT_LOCK at src/backend/storage/lmgr/proc.c:1487 is the grep-verified reporting path. The public identity/resource is mapped at src/backend/utils/activity/wait_event_names.txt:419. The instrumented operation is: Waiting to extend a relation. The lock manager could not grant the extend heavyweight lock immediately. ProcSleep reports the lock wait and parks the backend on the lock’s wait queue until owners release or the request is cancelled.

Normal or trouble?

  • Normal: Sub-second handoff during ordinary writes or planned DDL can be normal.
  • Investigate: Investigate once a user-facing wait breaches its latency objective, a blocking chain grows, or the root holder is idle in transaction.

Diagnostic SQL

Sessions waiting on Lock/extend
SELECT pid, backend_type, usename, datname, application_name,
       state, now() - query_start AS query_age,
       now() - xact_start AS xact_age,
       wait_event_type, wait_event,
       pg_blocking_pids(pid) AS blocking_pids,
       left(query, 160) AS query
FROM pg_stat_activity
WHERE wait_event_type = 'Lock'
  AND wait_event = 'extend'
ORDER BY query_age DESC NULLS LAST;
Current Lock cohort
SELECT wait_event, count(*) AS waiting_sessions,
       count(*) FILTER (WHERE state = 'active') AS active_waiters,
       max(now() - query_start) AS oldest_query
FROM pg_stat_activity
WHERE wait_event_type = 'Lock'
GROUP BY wait_event
ORDER BY waiting_sessions DESC, wait_event;
Lock and relation context for these sessions
SELECT a.pid, l.locktype, l.mode, l.granted, l.fastpath,
       d.datname, n.nspname, c.relname,
       l.page, l.tuple, l.virtualxid, l.transactionid,
       l.classid, l.objid, l.objsubid
FROM pg_stat_activity AS a
LEFT JOIN pg_locks AS l ON l.pid = a.pid
LEFT JOIN pg_database AS d ON d.oid = l.database
LEFT JOIN pg_class AS c
  ON c.oid = l.relation
 AND l.database = (
       SELECT oid FROM pg_database WHERE datname = current_database()
     )
LEFT JOIN pg_namespace AS n ON n.oid = c.relnamespace
WHERE a.wait_event_type = 'Lock'
  AND a.wait_event = 'extend'
ORDER BY a.pid, l.granted, l.locktype, l.mode;

Response

  1. Build the pg_blocking_pids graph to its root.
  2. Inspect the root holder’s state, transaction age, and business purpose.
  3. Choose cancellation, timeout, or workload sequencing only after identifying the safest root action.

Source evidence

4 - Lock: frozenid

Waiting to update pg_database.datfrozenxid and pg_database.datminmxid
PostgreSQL wait event dossier
ClassLock Eventfrozenid VersionsPG 13-18 Evidence3 source location(s)

Official description

Waiting to update pg_database.datfrozenxid and pg_database.datminmxid

PG 13 PG 14 PG 15 PG 16 PG 17 PG 18

Trigger mechanism

PG_WAIT_LOCK at src/backend/storage/lmgr/proc.c:1487 is the grep-verified reporting path. The public identity/resource is mapped at src/backend/utils/activity/wait_event_names.txt:420. The instrumented operation is: Waiting to update pg_database.datfrozenxid and pg_database.datminmxid. The lock manager could not grant the frozenid heavyweight lock immediately. ProcSleep reports the lock wait and parks the backend on the lock’s wait queue until owners release or the request is cancelled.

Normal or trouble?

  • Normal: Sub-second handoff during ordinary writes or planned DDL can be normal.
  • Investigate: Investigate once a user-facing wait breaches its latency objective, a blocking chain grows, or the root holder is idle in transaction.

Diagnostic SQL

Sessions waiting on Lock/frozenid
SELECT pid, backend_type, usename, datname, application_name,
       state, now() - query_start AS query_age,
       now() - xact_start AS xact_age,
       wait_event_type, wait_event,
       pg_blocking_pids(pid) AS blocking_pids,
       left(query, 160) AS query
FROM pg_stat_activity
WHERE wait_event_type = 'Lock'
  AND wait_event = 'frozenid'
ORDER BY query_age DESC NULLS LAST;
Current Lock cohort
SELECT wait_event, count(*) AS waiting_sessions,
       count(*) FILTER (WHERE state = 'active') AS active_waiters,
       max(now() - query_start) AS oldest_query
FROM pg_stat_activity
WHERE wait_event_type = 'Lock'
GROUP BY wait_event
ORDER BY waiting_sessions DESC, wait_event;
Lock and relation context for these sessions
SELECT a.pid, l.locktype, l.mode, l.granted, l.fastpath,
       d.datname, n.nspname, c.relname,
       l.page, l.tuple, l.virtualxid, l.transactionid,
       l.classid, l.objid, l.objsubid
FROM pg_stat_activity AS a
LEFT JOIN pg_locks AS l ON l.pid = a.pid
LEFT JOIN pg_database AS d ON d.oid = l.database
LEFT JOIN pg_class AS c
  ON c.oid = l.relation
 AND l.database = (
       SELECT oid FROM pg_database WHERE datname = current_database()
     )
LEFT JOIN pg_namespace AS n ON n.oid = c.relnamespace
WHERE a.wait_event_type = 'Lock'
  AND a.wait_event = 'frozenid'
ORDER BY a.pid, l.granted, l.locktype, l.mode;

Response

  1. Build the pg_blocking_pids graph to its root.
  2. Inspect the root holder’s state, transaction age, and business purpose.
  3. Choose cancellation, timeout, or workload sequencing only after identifying the safest root action.

Source evidence

5 - Lock: object

Waiting to acquire a lock on a non-relation database object
PostgreSQL wait event dossier
ClassLock Eventobject VersionsPG 13-18 Evidence3 source location(s)

Official description

Waiting to acquire a lock on a non-relation database object

PG 13 PG 14 PG 15 PG 16 PG 17 PG 18

Trigger mechanism

PG_WAIT_LOCK at src/backend/storage/lmgr/proc.c:1487 is the grep-verified reporting path. The public identity/resource is mapped at src/backend/utils/activity/wait_event_names.txt:426. The instrumented operation is: Waiting to acquire a lock on a non-relation database object. The lock manager could not grant the object heavyweight lock immediately. ProcSleep reports the lock wait and parks the backend on the lock’s wait queue until owners release or the request is cancelled.

Normal or trouble?

  • Normal: Sub-second handoff during ordinary writes or planned DDL can be normal.
  • Investigate: Investigate once a user-facing wait breaches its latency objective, a blocking chain grows, or the root holder is idle in transaction.

Diagnostic SQL

Sessions waiting on Lock/object
SELECT pid, backend_type, usename, datname, application_name,
       state, now() - query_start AS query_age,
       now() - xact_start AS xact_age,
       wait_event_type, wait_event,
       pg_blocking_pids(pid) AS blocking_pids,
       left(query, 160) AS query
FROM pg_stat_activity
WHERE wait_event_type = 'Lock'
  AND wait_event = 'object'
ORDER BY query_age DESC NULLS LAST;
Current Lock cohort
SELECT wait_event, count(*) AS waiting_sessions,
       count(*) FILTER (WHERE state = 'active') AS active_waiters,
       max(now() - query_start) AS oldest_query
FROM pg_stat_activity
WHERE wait_event_type = 'Lock'
GROUP BY wait_event
ORDER BY waiting_sessions DESC, wait_event;
Lock and relation context for these sessions
SELECT a.pid, l.locktype, l.mode, l.granted, l.fastpath,
       d.datname, n.nspname, c.relname,
       l.page, l.tuple, l.virtualxid, l.transactionid,
       l.classid, l.objid, l.objsubid
FROM pg_stat_activity AS a
LEFT JOIN pg_locks AS l ON l.pid = a.pid
LEFT JOIN pg_database AS d ON d.oid = l.database
LEFT JOIN pg_class AS c
  ON c.oid = l.relation
 AND l.database = (
       SELECT oid FROM pg_database WHERE datname = current_database()
     )
LEFT JOIN pg_namespace AS n ON n.oid = c.relnamespace
WHERE a.wait_event_type = 'Lock'
  AND a.wait_event = 'object'
ORDER BY a.pid, l.granted, l.locktype, l.mode;

Response

  1. Build the pg_blocking_pids graph to its root.
  2. Inspect the root holder’s state, transaction age, and business purpose.
  3. Choose cancellation, timeout, or workload sequencing only after identifying the safest root action.

Source evidence

6 - Lock: page

Waiting to acquire a lock on a page of a relation
PostgreSQL wait event dossier
ClassLock Eventpage VersionsPG 13-18 Evidence2 source location(s)

Official description

Waiting to acquire a lock on a page of a relation

PG 13 PG 14 PG 15 PG 16 PG 17 PG 18

Trigger mechanism

PG_WAIT_LOCK at src/backend/storage/lmgr/proc.c:1487 is the grep-verified reporting path. The public identity/resource is mapped at src/backend/utils/activity/wait_event_names.txt:421. The instrumented operation is: Waiting to acquire a lock on a page of a relation. The lock manager could not grant the page heavyweight lock immediately. ProcSleep reports the lock wait and parks the backend on the lock’s wait queue until owners release or the request is cancelled.

Normal or trouble?

  • Normal: Sub-second handoff during ordinary writes or planned DDL can be normal.
  • Investigate: Investigate once a user-facing wait breaches its latency objective, a blocking chain grows, or the root holder is idle in transaction.

Diagnostic SQL

Sessions waiting on Lock/page
SELECT pid, backend_type, usename, datname, application_name,
       state, now() - query_start AS query_age,
       now() - xact_start AS xact_age,
       wait_event_type, wait_event,
       pg_blocking_pids(pid) AS blocking_pids,
       left(query, 160) AS query
FROM pg_stat_activity
WHERE wait_event_type = 'Lock'
  AND wait_event = 'page'
ORDER BY query_age DESC NULLS LAST;
Current Lock cohort
SELECT wait_event, count(*) AS waiting_sessions,
       count(*) FILTER (WHERE state = 'active') AS active_waiters,
       max(now() - query_start) AS oldest_query
FROM pg_stat_activity
WHERE wait_event_type = 'Lock'
GROUP BY wait_event
ORDER BY waiting_sessions DESC, wait_event;
Lock and relation context for these sessions
SELECT a.pid, l.locktype, l.mode, l.granted, l.fastpath,
       d.datname, n.nspname, c.relname,
       l.page, l.tuple, l.virtualxid, l.transactionid,
       l.classid, l.objid, l.objsubid
FROM pg_stat_activity AS a
LEFT JOIN pg_locks AS l ON l.pid = a.pid
LEFT JOIN pg_database AS d ON d.oid = l.database
LEFT JOIN pg_class AS c
  ON c.oid = l.relation
 AND l.database = (
       SELECT oid FROM pg_database WHERE datname = current_database()
     )
LEFT JOIN pg_namespace AS n ON n.oid = c.relnamespace
WHERE a.wait_event_type = 'Lock'
  AND a.wait_event = 'page'
ORDER BY a.pid, l.granted, l.locktype, l.mode;

Response

  1. Build the pg_blocking_pids graph to its root.
  2. Inspect the root holder’s state, transaction age, and business purpose.
  3. Choose cancellation, timeout, or workload sequencing only after identifying the safest root action.

Source evidence

7 - Lock: relation

Waiting to acquire a lock on a relation
PostgreSQL wait event dossier
ClassLock Eventrelation VersionsPG 13-18 Evidence3 source location(s)

Official description

Waiting to acquire a lock on a relation

PG 13 PG 14 PG 15 PG 16 PG 17 PG 18

Trigger mechanism

PG_WAIT_LOCK at src/backend/storage/lmgr/proc.c:1487 is the grep-verified reporting path. The public identity/resource is mapped at src/backend/utils/activity/wait_event_names.txt:418. The instrumented operation is: Waiting to acquire a lock on a relation. The lock manager could not grant the relation heavyweight lock immediately. ProcSleep reports the lock wait and parks the backend on the lock’s wait queue until owners release or the request is cancelled.

Normal or trouble?

  • Normal: Sub-second handoff during ordinary writes or planned DDL can be normal.
  • Investigate: Investigate once a user-facing wait breaches its latency objective, a blocking chain grows, or the root holder is idle in transaction.

Diagnostic SQL

Sessions waiting on Lock/relation
SELECT pid, backend_type, usename, datname, application_name,
       state, now() - query_start AS query_age,
       now() - xact_start AS xact_age,
       wait_event_type, wait_event,
       pg_blocking_pids(pid) AS blocking_pids,
       left(query, 160) AS query
FROM pg_stat_activity
WHERE wait_event_type = 'Lock'
  AND wait_event = 'relation'
ORDER BY query_age DESC NULLS LAST;
Current Lock cohort
SELECT wait_event, count(*) AS waiting_sessions,
       count(*) FILTER (WHERE state = 'active') AS active_waiters,
       max(now() - query_start) AS oldest_query
FROM pg_stat_activity
WHERE wait_event_type = 'Lock'
GROUP BY wait_event
ORDER BY waiting_sessions DESC, wait_event;
Lock and relation context for these sessions
SELECT a.pid, l.locktype, l.mode, l.granted, l.fastpath,
       d.datname, n.nspname, c.relname,
       l.page, l.tuple, l.virtualxid, l.transactionid,
       l.classid, l.objid, l.objsubid
FROM pg_stat_activity AS a
LEFT JOIN pg_locks AS l ON l.pid = a.pid
LEFT JOIN pg_database AS d ON d.oid = l.database
LEFT JOIN pg_class AS c
  ON c.oid = l.relation
 AND l.database = (
       SELECT oid FROM pg_database WHERE datname = current_database()
     )
LEFT JOIN pg_namespace AS n ON n.oid = c.relnamespace
WHERE a.wait_event_type = 'Lock'
  AND a.wait_event = 'relation'
ORDER BY a.pid, l.granted, l.locktype, l.mode;

Response

  1. Build the pg_blocking_pids graph to its root.
  2. Inspect the root holder’s state, transaction age, and business purpose.
  3. Choose cancellation, timeout, or workload sequencing only after identifying the safest root action.

Source evidence

8 - Lock: spectoken

Waiting to acquire a speculative insertion lock
PostgreSQL wait event dossier
ClassLock Eventspectoken VersionsPG 13-18 Evidence3 source location(s)

Official description

Waiting to acquire a speculative insertion lock

PG 13 PG 14 PG 15 PG 16 PG 17 PG 18

Trigger mechanism

PG_WAIT_LOCK at src/backend/storage/lmgr/proc.c:1487 is the grep-verified reporting path. The public identity/resource is mapped at src/backend/utils/activity/wait_event_names.txt:425. The instrumented operation is: Waiting to acquire a speculative insertion lock. The lock manager could not grant the spectoken heavyweight lock immediately. ProcSleep reports the lock wait and parks the backend on the lock’s wait queue until owners release or the request is cancelled.

Normal or trouble?

  • Normal: Sub-second handoff during ordinary writes or planned DDL can be normal.
  • Investigate: Investigate once a user-facing wait breaches its latency objective, a blocking chain grows, or the root holder is idle in transaction.

Diagnostic SQL

Sessions waiting on Lock/spectoken
SELECT pid, backend_type, usename, datname, application_name,
       state, now() - query_start AS query_age,
       now() - xact_start AS xact_age,
       wait_event_type, wait_event,
       pg_blocking_pids(pid) AS blocking_pids,
       left(query, 160) AS query
FROM pg_stat_activity
WHERE wait_event_type = 'Lock'
  AND wait_event = 'spectoken'
ORDER BY query_age DESC NULLS LAST;
Current Lock cohort
SELECT wait_event, count(*) AS waiting_sessions,
       count(*) FILTER (WHERE state = 'active') AS active_waiters,
       max(now() - query_start) AS oldest_query
FROM pg_stat_activity
WHERE wait_event_type = 'Lock'
GROUP BY wait_event
ORDER BY waiting_sessions DESC, wait_event;
Lock and relation context for these sessions
SELECT a.pid, l.locktype, l.mode, l.granted, l.fastpath,
       d.datname, n.nspname, c.relname,
       l.page, l.tuple, l.virtualxid, l.transactionid,
       l.classid, l.objid, l.objsubid
FROM pg_stat_activity AS a
LEFT JOIN pg_locks AS l ON l.pid = a.pid
LEFT JOIN pg_database AS d ON d.oid = l.database
LEFT JOIN pg_class AS c
  ON c.oid = l.relation
 AND l.database = (
       SELECT oid FROM pg_database WHERE datname = current_database()
     )
LEFT JOIN pg_namespace AS n ON n.oid = c.relnamespace
WHERE a.wait_event_type = 'Lock'
  AND a.wait_event = 'spectoken'
ORDER BY a.pid, l.granted, l.locktype, l.mode;

Response

  1. Build the pg_blocking_pids graph to its root.
  2. Inspect the root holder’s state, transaction age, and business purpose.
  3. Choose cancellation, timeout, or workload sequencing only after identifying the safest root action.

Source evidence

9 - Lock: transactionid

Waiting for a transaction to finish
PostgreSQL wait event dossier
ClassLock Eventtransactionid VersionsPG 13-18 Evidence3 source location(s)

Official description

Waiting for a transaction to finish

PG 13 PG 14 PG 15 PG 16 PG 17 PG 18

Trigger mechanism

PG_WAIT_LOCK at src/backend/storage/lmgr/proc.c:1487 is the grep-verified reporting path. The public identity/resource is mapped at src/backend/utils/activity/wait_event_names.txt:423. The instrumented operation is: Waiting for a transaction to finish. The lock manager could not grant the transactionid heavyweight lock immediately. ProcSleep reports the lock wait and parks the backend on the lock’s wait queue until owners release or the request is cancelled.

Normal or trouble?

  • Normal: Sub-second handoff during ordinary writes or planned DDL can be normal.
  • Investigate: Investigate once a user-facing wait breaches its latency objective, a blocking chain grows, or the root holder is idle in transaction.

Diagnostic SQL

Sessions waiting on Lock/transactionid
SELECT pid, backend_type, usename, datname, application_name,
       state, now() - query_start AS query_age,
       now() - xact_start AS xact_age,
       wait_event_type, wait_event,
       pg_blocking_pids(pid) AS blocking_pids,
       left(query, 160) AS query
FROM pg_stat_activity
WHERE wait_event_type = 'Lock'
  AND wait_event = 'transactionid'
ORDER BY query_age DESC NULLS LAST;
Current Lock cohort
SELECT wait_event, count(*) AS waiting_sessions,
       count(*) FILTER (WHERE state = 'active') AS active_waiters,
       max(now() - query_start) AS oldest_query
FROM pg_stat_activity
WHERE wait_event_type = 'Lock'
GROUP BY wait_event
ORDER BY waiting_sessions DESC, wait_event;
Lock and relation context for these sessions
SELECT a.pid, l.locktype, l.mode, l.granted, l.fastpath,
       d.datname, n.nspname, c.relname,
       l.page, l.tuple, l.virtualxid, l.transactionid,
       l.classid, l.objid, l.objsubid
FROM pg_stat_activity AS a
LEFT JOIN pg_locks AS l ON l.pid = a.pid
LEFT JOIN pg_database AS d ON d.oid = l.database
LEFT JOIN pg_class AS c
  ON c.oid = l.relation
 AND l.database = (
       SELECT oid FROM pg_database WHERE datname = current_database()
     )
LEFT JOIN pg_namespace AS n ON n.oid = c.relnamespace
WHERE a.wait_event_type = 'Lock'
  AND a.wait_event = 'transactionid'
ORDER BY a.pid, l.granted, l.locktype, l.mode;

Response

  1. Build the pg_blocking_pids graph to its root.
  2. Inspect the root holder’s state, transaction age, and business purpose.
  3. Choose cancellation, timeout, or workload sequencing only after identifying the safest root action.

Source evidence

10 - Lock: tuple

Waiting to acquire a lock on a tuple
PostgreSQL wait event dossier
ClassLock Eventtuple VersionsPG 13-18 Evidence3 source location(s)

Official description

Waiting to acquire a lock on a tuple

PG 13 PG 14 PG 15 PG 16 PG 17 PG 18

Trigger mechanism

PG_WAIT_LOCK at src/backend/storage/lmgr/proc.c:1487 is the grep-verified reporting path. The public identity/resource is mapped at src/backend/utils/activity/wait_event_names.txt:422. The instrumented operation is: Waiting to acquire a lock on a tuple. The lock manager could not grant the tuple heavyweight lock immediately. ProcSleep reports the lock wait and parks the backend on the lock’s wait queue until owners release or the request is cancelled.

Normal or trouble?

  • Normal: Sub-second handoff during ordinary writes or planned DDL can be normal.
  • Investigate: Investigate once a user-facing wait breaches its latency objective, a blocking chain grows, or the root holder is idle in transaction.

Diagnostic SQL

Sessions waiting on Lock/tuple
SELECT pid, backend_type, usename, datname, application_name,
       state, now() - query_start AS query_age,
       now() - xact_start AS xact_age,
       wait_event_type, wait_event,
       pg_blocking_pids(pid) AS blocking_pids,
       left(query, 160) AS query
FROM pg_stat_activity
WHERE wait_event_type = 'Lock'
  AND wait_event = 'tuple'
ORDER BY query_age DESC NULLS LAST;
Current Lock cohort
SELECT wait_event, count(*) AS waiting_sessions,
       count(*) FILTER (WHERE state = 'active') AS active_waiters,
       max(now() - query_start) AS oldest_query
FROM pg_stat_activity
WHERE wait_event_type = 'Lock'
GROUP BY wait_event
ORDER BY waiting_sessions DESC, wait_event;
Lock and relation context for these sessions
SELECT a.pid, l.locktype, l.mode, l.granted, l.fastpath,
       d.datname, n.nspname, c.relname,
       l.page, l.tuple, l.virtualxid, l.transactionid,
       l.classid, l.objid, l.objsubid
FROM pg_stat_activity AS a
LEFT JOIN pg_locks AS l ON l.pid = a.pid
LEFT JOIN pg_database AS d ON d.oid = l.database
LEFT JOIN pg_class AS c
  ON c.oid = l.relation
 AND l.database = (
       SELECT oid FROM pg_database WHERE datname = current_database()
     )
LEFT JOIN pg_namespace AS n ON n.oid = c.relnamespace
WHERE a.wait_event_type = 'Lock'
  AND a.wait_event = 'tuple'
ORDER BY a.pid, l.granted, l.locktype, l.mode;

Response

  1. Build the pg_blocking_pids graph to its root.
  2. Inspect the root holder’s state, transaction age, and business purpose.
  3. Choose cancellation, timeout, or workload sequencing only after identifying the safest root action.

Source evidence

11 - Lock: userlock

Waiting to acquire a user lock
PostgreSQL wait event dossier
ClassLock Eventuserlock VersionsPG 13-18 Evidence3 source location(s)

Official description

Waiting to acquire a user lock

PG 13 PG 14 PG 15 PG 16 PG 17 PG 18

Trigger mechanism

PG_WAIT_LOCK at src/backend/storage/lmgr/proc.c:1487 is the grep-verified reporting path. The public identity/resource is mapped at src/backend/utils/activity/wait_event_names.txt:427. The instrumented operation is: Waiting to acquire a user lock. The lock manager could not grant the userlock heavyweight lock immediately. ProcSleep reports the lock wait and parks the backend on the lock’s wait queue until owners release or the request is cancelled.

Normal or trouble?

  • Normal: Sub-second handoff during ordinary writes or planned DDL can be normal.
  • Investigate: Investigate once a user-facing wait breaches its latency objective, a blocking chain grows, or the root holder is idle in transaction.

Diagnostic SQL

Sessions waiting on Lock/userlock
SELECT pid, backend_type, usename, datname, application_name,
       state, now() - query_start AS query_age,
       now() - xact_start AS xact_age,
       wait_event_type, wait_event,
       pg_blocking_pids(pid) AS blocking_pids,
       left(query, 160) AS query
FROM pg_stat_activity
WHERE wait_event_type = 'Lock'
  AND wait_event = 'userlock'
ORDER BY query_age DESC NULLS LAST;
Current Lock cohort
SELECT wait_event, count(*) AS waiting_sessions,
       count(*) FILTER (WHERE state = 'active') AS active_waiters,
       max(now() - query_start) AS oldest_query
FROM pg_stat_activity
WHERE wait_event_type = 'Lock'
GROUP BY wait_event
ORDER BY waiting_sessions DESC, wait_event;
Lock and relation context for these sessions
SELECT a.pid, l.locktype, l.mode, l.granted, l.fastpath,
       d.datname, n.nspname, c.relname,
       l.page, l.tuple, l.virtualxid, l.transactionid,
       l.classid, l.objid, l.objsubid
FROM pg_stat_activity AS a
LEFT JOIN pg_locks AS l ON l.pid = a.pid
LEFT JOIN pg_database AS d ON d.oid = l.database
LEFT JOIN pg_class AS c
  ON c.oid = l.relation
 AND l.database = (
       SELECT oid FROM pg_database WHERE datname = current_database()
     )
LEFT JOIN pg_namespace AS n ON n.oid = c.relnamespace
WHERE a.wait_event_type = 'Lock'
  AND a.wait_event = 'userlock'
ORDER BY a.pid, l.granted, l.locktype, l.mode;

Response

  1. Build the pg_blocking_pids graph to its root.
  2. Inspect the root holder’s state, transaction age, and business purpose.
  3. Choose cancellation, timeout, or workload sequencing only after identifying the safest root action.

Source evidence

12 - Lock: virtualxid

Waiting to acquire a virtual transaction ID lock
PostgreSQL wait event dossier
ClassLock Eventvirtualxid VersionsPG 13-18 Evidence3 source location(s)

Official description

Waiting to acquire a virtual transaction ID lock

PG 13 PG 14 PG 15 PG 16 PG 17 PG 18

Trigger mechanism

PG_WAIT_LOCK at src/backend/storage/lmgr/proc.c:1487 is the grep-verified reporting path. The public identity/resource is mapped at src/backend/utils/activity/wait_event_names.txt:424. The instrumented operation is: Waiting to acquire a virtual transaction ID lock. The lock manager could not grant the virtualxid heavyweight lock immediately. ProcSleep reports the lock wait and parks the backend on the lock’s wait queue until owners release or the request is cancelled.

Normal or trouble?

  • Normal: Sub-second handoff during ordinary writes or planned DDL can be normal.
  • Investigate: Investigate once a user-facing wait breaches its latency objective, a blocking chain grows, or the root holder is idle in transaction.

Diagnostic SQL

Sessions waiting on Lock/virtualxid
SELECT pid, backend_type, usename, datname, application_name,
       state, now() - query_start AS query_age,
       now() - xact_start AS xact_age,
       wait_event_type, wait_event,
       pg_blocking_pids(pid) AS blocking_pids,
       left(query, 160) AS query
FROM pg_stat_activity
WHERE wait_event_type = 'Lock'
  AND wait_event = 'virtualxid'
ORDER BY query_age DESC NULLS LAST;
Current Lock cohort
SELECT wait_event, count(*) AS waiting_sessions,
       count(*) FILTER (WHERE state = 'active') AS active_waiters,
       max(now() - query_start) AS oldest_query
FROM pg_stat_activity
WHERE wait_event_type = 'Lock'
GROUP BY wait_event
ORDER BY waiting_sessions DESC, wait_event;
Lock and relation context for these sessions
SELECT a.pid, l.locktype, l.mode, l.granted, l.fastpath,
       d.datname, n.nspname, c.relname,
       l.page, l.tuple, l.virtualxid, l.transactionid,
       l.classid, l.objid, l.objsubid
FROM pg_stat_activity AS a
LEFT JOIN pg_locks AS l ON l.pid = a.pid
LEFT JOIN pg_database AS d ON d.oid = l.database
LEFT JOIN pg_class AS c
  ON c.oid = l.relation
 AND l.database = (
       SELECT oid FROM pg_database WHERE datname = current_database()
     )
LEFT JOIN pg_namespace AS n ON n.oid = c.relnamespace
WHERE a.wait_event_type = 'Lock'
  AND a.wait_event = 'virtualxid'
ORDER BY a.pid, l.granted, l.locktype, l.mode;

Response

  1. Build the pg_blocking_pids graph to its root.
  2. Inspect the root holder’s state, transaction age, and business purpose.
  3. Choose cancellation, timeout, or workload sequencing only after identifying the safest root action.

Source evidence