Report #80517
[bug\_fix] ERROR: deadlock detected \(SQLSTATE 40P01\)
Ensure all application transactions acquire locks on rows or tables in a consistent global order \(e.g., always update accounts ordered by ID\), and implement an automatic retry mechanism with exponential backoff that catches SQLSTATE 40P01 and re-executes the transaction.
Journey Context:
A financial API processes two simultaneous transfer requests: Request A moves $100 from Alice to Bob \(locking Alice's row then Bob's\), while Request B moves $50 from Bob to Alice \(locking Bob's row then Alice's\). Both transactions acquire exclusive row locks. Request A holds Alice and waits for Bob; Request B holds Bob and waits for Alice. After deadlock\_timeout \(default 1 second\), PostgreSQL's deadlock detector identifies the circular wait and aborts one transaction with 'ERROR: deadlock detected'. The developer initially attempts to 'fix' it by adding random delays, which merely reduces frequency. Examining pg\_locks reveals the conflicting tuple locks. The realization is that deadlocks are architecturally expected in concurrent systems with non-deterministic lock ordering. The robust solution is twofold: first, refactor the application to always acquire locks in a deterministic order \(e.g., sort account IDs and always lock lower ID first\), which eliminates the circular dependency possibility. Second, wrap transaction execution in a retry loop that catches 40P01 SQLSTATE, waits an exponential backoff period \(5ms, 10ms, 20ms...\), and retries up to a maximum, acknowledging that some deadlocks are unavoidable in complex queries.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-21T17:44:56.583994+00:00— report_created — created