Database Administration — Fri Apr 17

← Home | ← database-admin

PostgreSQL - DBA Toolkit - Deadlocks

Fri Apr 17 — Track deadlock counts per database, identify tables with active lock contention, and surface the transactions involved.
#postgresql #dba #monitoring #deadlocks #locking #troubleshooting #tool-kit

They Both Needed Each Other’s Lock

A deadlock is not a random failure.

Two transactions each hold a lock the other one needs. Neither can move forward. PostgreSQL detects the cycle, picks one transaction as the victim, rolls it back, and lets the other proceed. The application receives an error. Most retry silently.

Nobody investigated. The same access pattern runs again an hour later. The deadlock happens again.

This script surfaces deadlock counts from pg_stat_database, active lock contention from pg_locks, and running transactions involved in waits so you can find the pattern and fix it.

Deadlocks.sql
    Loading…
  

Reading the Results

Deadlock Counts (pg_stat_database)

ColumnWhat It Tells You
datnameDatabase name
deadlocksCumulative deadlock count since last pg_stat_reset()
conflictsQuery conflicts with recovery on standby replicas
temp_filesTemp files created — a sign of memory pressure during sorts or hash joins

Active Lock Contention (pg_locks + pg_stat_activity)

ColumnWhat It Tells You
pidProcess ID of the waiting session
usenameDatabase user
queryThe SQL being blocked
locktypeType of lock requested — relation, tuple, transactionid, etc.
modeLock mode requested — e.g. RowExclusiveLock, ShareRowExclusiveLock
grantedfalse means this session is waiting
wait_eventWhat specifically the session is waiting on

What to Watch For

SignalWhat It Means
deadlocks count climbingA recurring access pattern is causing repeated deadlocks — needs investigation
Same table appearing repeatedly in lock waitsIndex design or transaction ordering problem on that table
locktype = relation on a busy tableTable-level lock — DDL, TRUNCATE, or LOCK TABLE is running against it
mode = AccessExclusiveLockThe most aggressive lock — blocks all readers and writers
Many rows with granted = falseA single blocker is causing a queue — find and resolve the root transaction
Deadlocks spiking after a deploymentApplication code change introduced conflicting transaction ordering

Reducing Deadlocks

Deadlocks are almost always fixable at the application level:

  • Access tables in the same order across all transactions — if transaction A always locks orders before line_items, transaction B should too
  • Keep transactions short — commit as soon as the work is done, do not hold locks across API calls or user input
  • Use SELECT ... FOR UPDATE explicitly when you know you will modify a row — avoids the lock upgrade pattern that causes deadlocks
  • Retry on deadlock (error code 40P01) — but treat it as a monitoring signal, not a permanent fix

To enable deadlock logging in PostgreSQL:

EnableDeadlockLogging.sql
    Loading…
  

This logs any lock wait exceeding 1 second and all deadlock events to the PostgreSQL log.

Gareth Winterman