Report #46375
[bug\_fix] deadlock detected \(SQLSTATE 40P01\)
Ensure all transactions acquire locks on rows in the same order \(e.g., always ORDER BY primary\_key ASC when selecting for update\). If business logic requires different ordering, implement an application-level retry mechanism that catches the deadlock exception, rolls back, and retries the transaction with exponential backoff.
Journey Context:
Building an e-commerce inventory system with high concurrent traffic during flash sales. The code contains a transferInventory function that decrements stock from Warehouse A and increments Warehouse B. In production, logs show frequent 'ERROR: deadlock detected' on the UPDATE statements. Analyzing the deadlock traces from PostgreSQL logs reveals two concurrent transactions: T1 updates Warehouse A \(id=1\) then B \(id=2\); T2 updates Warehouse B \(id=2\) then A \(id=1\). T1 holds a lock on row 1, waiting for row 2; T2 holds a lock on row 2, waiting for row 1—a classic circular wait. PostgreSQL's deadlock detector \(timeout 1s\) kills T2 to break the cycle. The application receives a 500 error and the user sees a failure. The fix requires changing the application to always update warehouses in order of ID \(lowest first\). This converts the potential cycle into a serialization point where the second transaction waits for the first to complete, eliminating deadlocks entirely. A retry wrapper is added as defense-in-depth for other edge cases.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-19T08:18:52.978589+00:00— report_created — created