Cookbook recipe

Lock Monitoring Gaps: Deadlocks Not Logged

Applies to PostgreSQL 13–17 Last reviewed May 2026 Grounded in source
Estimated investigation4 min

Scenario

Scenario An application reports intermittent ERROR: deadlock detected errors in its logs, but the DBA finds no corresponding entries in the PostgreSQL server log. The log_lock_waits setting is off, so long lock waits are invisible. The…

Investigation Path

Scenario

An application reports intermittent ERROR: deadlock detected errors in its logs, but the DBA finds no corresponding entries in the PostgreSQL server log. The log_lock_waits setting is off, so long lock waits are invisible. The deadlock_timeout is set to 10 seconds (default), meaning PostgreSQL only detects deadlocks after 10 seconds — long enough for requests to time out first. The team has no visibility into which queries are blocking each other.

How to Identify

Conditions:

  • Application logs show deadlock detected errors but PostgreSQL log has no lock-related entries
  • log_lock_waits = off in pg_settings
  • deadlock_timeout = 10s (default) — applications time out before deadlock is detected
  • pg_locks shows granted = false entries (waiting for locks) but no alerting
  • pg_stat_activity shows sessions with wait_event_type = 'Lock'

Analysis Steps

-- Check lock-related settings:
SELECT name, setting, unit, context
FROM pg_settings
WHERE name IN ('log_lock_waits', 'deadlock_timeout', 'lock_timeout', 'statement_timeout')
ORDER BY name;

-- Find currently waiting sessions:
SELECT pid, usename, wait_event_type, wait_event, left(query, 80) AS query
FROM pg_stat_activity
WHERE wait_event_type = 'Lock';

-- Find active locks and which are waiting:
SELECT
    locktype, relation::regclass, mode, granted,
    pid, transactionid
FROM pg_locks
WHERE NOT granted
ORDER BY pid;

-- Join pg_locks with pg_stat_activity to see full picture:
SELECT
    l.pid,
    a.usename,
    l.locktype,
    l.relation::regclass AS table_name,
    l.mode,
    l.granted,
    left(a.query, 80) AS query
FROM pg_locks l
JOIN pg_stat_activity a USING (pid)
WHERE NOT l.granted
ORDER BY l.pid;

Pitfalls

  • log_lock_waits = off (default) means PostgreSQL never logs lock waits — the DBA is flying blind during locking incidents. Always enable this in production.
  • deadlock_timeout = 1s is recommended for most production workloads — the default 10s means deadlocks linger for 10 seconds before being detected.
  • lock_timeout (per session/statement) is different from deadlock_timeout (cluster-wide deadlock detection interval). Setting lock_timeout = '5s' causes a statement to error immediately if it waits more than 5s for a lock.
  • pg_locks only shows current locks — historical data requires log_lock_waits = on which writes to server logs.
  • Advisory locks (locktype = 'advisory') appear in pg_locks and can block just like relation locks. Often forgotten in application lock analysis.
  • Row-level locks (from SELECT FOR UPDATE) appear as relation locks in pg_locks but the specific row isn’t shown — use pg_blocking_pids() to trace them.

Resolution Approach

Enable log_lock_waits = on and reduce deadlock_timeout to 1 second. Set lock_timeout at the application or role level to prevent indefinite lock waits. Use pg_blocking_pids() for real-time lock chain diagnosis.

This is a Pro lesson

Get every Learning Pathway and cookbook recipe — grounded in PostgreSQL source code, with diagnostics, fixes, and prevention for each topic.

Continue this lesson to learn:

  • Mitigation Actions
  • All 36 Learning Pathway lessons
  • 170+ cookbook recipes
  • Source-grounded diagnostics & fixes

Secure checkout Cancel anytime Source-grounded

Career Impact

This scenario builds production judgment and operational confidence under pressure.

Open Career Dashboard →

Keep going

Related & next steps

Was this helpful?

← All cookbook recipes