DeadlockDetected
Runbook: DeadlockDetected Alert
Alert Details
- Alert Name: DeadlockDetected
- Expression:
increase(pg_stat_database_deadlocks{datname!~"template.*"}[30m]) > 0
Description
This alert triggers when the deadlock counter of a database increases and stays active for 30 minutes. A deadlock occurs when two or more transactions are each waiting for a lock held by the other, causing all of them to block indefinitely. PostgreSQL detects this automatically and aborts one of the transactions to break the cycle.
Possible Causes
- Two transactions updating the same rows in opposite order (classic deadlock pattern)
- Application-level retry logic re-entering a deadlock situation repeatedly
- Bulk operations (imports, batch updates) conflicting with concurrent application writes
- Missing or overly broad locks causing unintended contention
Troubleshooting Steps
1. Check how many deadlocks have occurred per database
|
|
High deadlocks on a specific database narrows the scope. Note which database is affected before proceeding.
2. Check PostgreSQL logs for deadlock details
PostgreSQL logs the full deadlock graph when it aborts a transaction. Look for lines containing deadlock detected in the database logs:
ERROR: deadlock detected
DETAIL: Process 1234 waits for ShareLock on transaction 5678; blocked by process 9012.
Process 9012 waits for ShareLock on transaction 5678; blocked by process 1234.
HINT: See server log for query details.The log entries include the exact queries and the lock order that caused the deadlock. This is the most reliable way to identify the root cause.
3. Identify currently waiting/blocked transactions
If deadlocks are still actively occurring, check for transactions that are blocked on locks right now:
|
|
4. Identify which tables are involved
|
|
Knowing which tables are involved helps identify the application paths causing the deadlock.
5. Terminate blocking processes if deadlocks are ongoing
If a transaction has been blocking others for an unacceptable duration, terminate it:
|
|
PostgreSQL will already have aborted one side of a completed deadlock automatically — only use this if blocking is still ongoing.
6. Long-term fix
Deadlocks are an application-level problem. After identifying the queries involved, the fix is usually one of:
- Enforce a consistent lock order — ensure all transactions always acquire locks on the same tables/rows in the same order
- Use
SELECT ... FOR UPDATEexplicitly — acquire row-level locks upfront at the start of a transaction rather than implicitly during updates - Reduce transaction scope — keep transactions short so they hold locks for less time
- Use advisory locks for application-level mutual exclusion on operations that repeatedly deadlock