Report #46516
[bug\_fix] ERROR: deadlock detected
Implement an application-level retry mechanism that catches SQLSTATE 40P01, rolls back the current transaction, and retries with exponential backoff. For a structural fix, ensure all application code acquires row or table locks in a strict global order \(e.g., always lock account rows ordered by account\_id ASC\) to prevent the circular wait condition.
Journey Context:
A developer builds a peer-to-peer payment API. Two users simultaneously initiate transfers: Tx1 sends $100 from Alice to Bob, and Tx2 sends $50 from Bob to Alice. Both use \`SELECT ... FOR UPDATE\` to lock the sender's account before updating the recipient. Tx1 locks Alice, Tx2 locks Bob. Tx1 then tries to lock Bob \(blocked by Tx2\), and Tx2 tries to lock Alice \(blocked by Tx1\). Postgres detects the circular dependency, terminates Tx2 with "ERROR: deadlock detected", and logs the waiting processes in \`postgresql.log\`. The developer initially suspects a bug in the UPDATE logic. After analyzing the logs showing "Process 12345 waits for ShareLock on transaction 67890; blocked by process 12346", they recognize the race condition. They implement a retry decorator in their Python code using \`tenacity\` that catches \`psycopg2.errors.DeadlockDetected\` \(40P01\), rolls back, and retries. They also refactor the locking logic to \`SELECT ... FROM accounts WHERE account\_id IN \($1, $2\) ORDER BY account\_id FOR UPDATE\`, ensuring both transactions always acquire locks in the same order, preventing deadlocks structurally.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-19T08:32:57.776855+00:00— report_created — created