Report #5367
[bug\_fix] ERROR: deadlock detected \(SQLSTATE 40P01\)
Enforce a strict global lock acquisition order in all transactions \(e.g., always lock table A before table B\), or wrap the transaction in a retry loop that catches SQLSTATE 40P01, rolls back, and re-executes with exponential backoff. Root cause: Two concurrent transactions hold row-level locks on resources the other needs, creating a circular wait; Postgres's deadlock detector aborts one transaction \(the victim\) to break the cycle.
Journey Context:
Your payment service occasionally logs "ERROR: deadlock detected" under high concurrency. The code updates two accounts in a transfer: UPDATE accounts SET balance = balance - 100 WHERE id = 1; then UPDATE ... WHERE id = 2;. You inspect pg\_stat\_activity during an incident and see two active transactions: T1 holds a lock on row 1 waiting for row 2, and T2 holds a lock on row 2 waiting for row 1. This circular dependency triggers the deadlock detector. You realize the application doesn't guarantee the order of updates; sometimes it updates the payer first, sometimes the payee depending on JSON payload order. You refactor the code to always sort account IDs and perform updates in ascending order. This guarantees that all transactions acquire locks in the same sequence, making circular waits impossible. Alternatively, you implement a retry decorator that catches 40P01, waits 100ms, and retries up to 3 times. After deployment, the deadlock alerts stop because the consistent ordering prevents the cycle, and any remaining rare deadlocks \(from ad-hoc queries\) are handled by the retry logic.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-15T21:09:56.314565+00:00— report_created — created