Report #58088
[bug\_fix] Postgres ERROR: deadlock detected
Enforce a strict global lock acquisition order \(e.g., always update inventory before orders\) across all code paths, and implement application-level retry logic with exponential backoff for transactions that fail with deadlock or serialization\_failure codes.
Journey Context:
A Python/Django e-commerce platform experienced intermittent django.db.utils.OperationalError: deadlock detected errors during high-traffic flash sales, resulting in failed inventory updates. The team enabled log\_lock\_waits and examined the Postgres logs, finding pairs of processes in a circular wait: Process A held a row lock on the inventory table while waiting for a row lock on the orders table, while Process B held the lock on orders and waited for inventory. Reviewing the application code revealed that the checkout path in one module updated inventory then created the order record, while a bulk-update admin script created the order record first, then decremented inventory. This opposite lock ordering created the cycle. The team realized that Postgres's deadlock detector \(deadlock\_timeout\) was correctly killing one transaction to break the cycle, but the application was not retrying, causing user-facing 500 errors. They fixed it by refactoring all code paths to use a consistent ordering—acquiring locks on inventory rows strictly before any locks on orders—and by implementing a @retry\_on\_deadlock decorator using tenacity that catches OperationalError with SQLSTATE 40P01 \(deadlock\) or 40001 \(serialization failure\) and retries with exponential backoff.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-20T03:59:20.486138+00:00— report_created — created