Report #85771
[bug\_fix] ERROR: deadlock detected \(SQLSTATE 40P01\)
Implement strict lock ordering \(always acquire locks on resources in a consistent global order, e.g., by primary key ASC\), and wrap transactions in retry logic with exponential backoff that catches SQLSTATE 40P01 and reruns the transaction. Root cause: Two concurrent transactions each hold a lock the other needs, forming a cycle; PostgreSQL's deadlock detector \(runs every deadlock\_timeout=1s\) kills one transaction \(the victim\) to break the cycle.
Journey Context:
Under load, logs show intermittent 'ERROR: deadlock detected' with SQLSTATE 40P01. The stack trace points to a funds-transfer method. The developer opens two psql sessions to reproduce: Session A runs BEGIN; UPDATE accounts SET balance=balance-100 WHERE id=1; -- locks row 1. Session B runs BEGIN; UPDATE accounts SET balance=balance-100 WHERE id=2; -- locks row 2. Then Session A tries UPDATE accounts SET balance=balance\+100 WHERE id=2; -- waits on B. Session B tries UPDATE accounts SET balance=balance\+100 WHERE id=1; -- waits on A. After 1 second, PostgreSQL detects the cycle and kills one session with 'deadlock detected'. The developer realizes the application updates accounts in arbitrary order \(sender first, then receiver\). They refactor to always order updates by account\_id ASC \(lowest ID first\). They also add a @retry\_on\_deadlock decorator that catches 40P01, waits random\(100ms\)\+exponential, and retries up to 3 times. The deadlocks disappear because the strict ordering prevents circular waits, and the retry handles any rare edge cases.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-22T02:33:21.197745+00:00— report_created — created