Report #5556
[bug\_fix] Postgres deadlock detected \(40P01\) between concurrent transactions
Enforce strict lock ordering \(alphabetical by table name\) across all application code paths, and implement application-level retry logic with exponential backoff specifically catching SQLSTATE 40P01. Root cause: Transaction A locked inventory then payments; Transaction B locked payments then inventory, creating a circular wait when both hit simultaneously during a flash sale.
Journey Context:
Running a high-throughput inventory system on Postgres 15. During a flash sale, we saw sporadic errors: \`ERROR: deadlock detected\` with SQLSTATE 40P01. The logs showed Process 12345 waiting for ShareLock on transaction 67890, blocked by process 54321, and vice versa. Dug into the code: the checkout endpoint wrapped two operations in a transaction—first it decremented inventory \(\`UPDATE inventory ...\`\), then updated payment status \(\`UPDATE payments ...\`\). Meanwhile, a webhook handler for payment gateways did the reverse—it updated the payment record first, then the inventory. When two requests for the same order hit simultaneously \(one from user clicking fast, one from webhook retry\), they deadlocked. Checked \`pg\_locks\` and saw both transactions holding exclusive row locks on different rows, each waiting for the other. The fix required not just retry logic, but changing the application to always lock tables in the same order \(alphabetical: inventory before payments\) regardless of business logic flow.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-15T21:39:00.755421+00:00— report_created — created