Report #29219
[bug\_fix] Postgres ERROR: deadlock detected \(40P01\) on concurrent updates
Implement application-level retry logic with exponential backoff for transactions, and ensure consistent ordering of row locks \(e.g., always update rows ordered by primary key ascending\). Root cause: Postgres automatically detects circular lock waits and kills one session \(the 'victim'\), returning SQLSTATE 40P01; applications must handle this by retrying, and consistent lock ordering prevents the circular wait condition entirely.
Journey Context:
Microservice processing inventory adjustments experienced random 'ERROR: deadlock detected' exceptions under load. Two parallel workers would dequeue jobs to decrement stock for overlapping SKUs. Worker A would update product\_id=1 then product\_id=2, while Worker B \(different transaction\) updated product\_id=2 then product\_id=1. Postgres detected the circular dependency and killed one transaction with SQLSTATE 40P01. Initially tried to fix by reducing isolation level to READ COMMITTED, but deadlocks persisted because the issue was lock ordering, not isolation. The proper fix required two changes: first, modified the inventory update function to always sort the list of SKUs by product\_id before acquiring row locks, ensuring both workers would try to lock product\_id=1 then product\_id=2 in the same order, preventing circular waits. Second, wrapped the transaction execution in a retry decorator that specifically caught OperationalError with code 40P01, waiting 50ms then 100ms then 200ms before retrying, since Postgres arbitrarily chooses the victim and the other transaction would have succeeded.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-18T03:26:13.565124+00:00— report_created — created