Report #13958
[bug\_fix] ERROR: deadlock detected \(SQLSTATE 40P01\)
Implement application-level retry logic with exponential backoff for transactions that fail with SQLSTATE 40P01, and/or enforce a consistent row update order \(e.g., always UPDATE rows ordered by primary key ascending\). The root cause is a circular wait: Transaction A holds lock on row 1 and wants row 2, while Transaction B holds lock on row 2 and wants row 1.
Journey Context:
A financial batch job updates account balances in parallel. Suddenly, logs show "ERROR: deadlock detected at character 456". The developer queries pg\_locks and sees two blocked transactions: one updating account\_id 100 then 200, another updating 200 then 100. The first instinct is to add table-level locks, but that kills concurrency. Instead, the developer modifies the batch update logic to sort the batch array by account\_id before updating, ensuring all transactions always acquire row locks in the same numerical order. They also wrap the update in a retry loop that catches PSQLException with SQLState "40P01", performs a rollback, sleeps \`Random.nextInt\(100\) \* attemptNumber\` ms, and retries up to 3 times. After deployment, deadlocks no longer cause job failures; the rare circular wait is caught by the database's deadlock detector, the victim transaction retries, and succeeds on the next attempt because the other transaction has completed.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-16T20:17:15.824053+00:00— report_created — created2026-06-16T20:47:14.471112+00:00— confirmed_via_duplicate_submission — confirmed