Report #28872
[bug\_fix] ERROR: deadlock detected \(SQLSTATE 40P01\)
Implement application-level retry logic with exponential backoff specifically for SQLSTATE 40P01. Root cause: Two concurrent transactions lock resources in opposite order \(e.g., T1 locks row A then B, T2 locks row B then A\), forming a cycle. PostgreSQL detects this and aborts one transaction to break the deadlock. The fix works because retrying allows the aborted transaction to run after the other completes, eliminating the circular wait.
Journey Context:
An e-commerce application processed order updates with a method annotated @Transactional. The method first updated inventory \(UPDATE products SET stock=stock-1 WHERE id=?\) and then updated the order status \(UPDATE orders SET status='shipped' WHERE id=?\). Under high load during a flash sale, logs filled with "ERROR: deadlock detected." Analyzing pg\_stat\_activity revealed two concurrent sessions: Transaction 1 held a lock on products.id=123 and was waiting for a lock on orders.id=456, while Transaction 2 held a lock on orders.id=456 \(from an earlier step in its workflow\) and was waiting for products.id=123. This classic circular dependency caused PostgreSQL to kill one transaction. The team first tried reordering all SQL statements to always lock products then orders in every code path, but with distributed microservices this was impossible to enforce globally. The correct fix was to implement Spring Retry with @Retryable on the service method, specifically including TransientDataAccessException \(which wraps SQLSTATE 40P01\) with maxAttempts=3 and exponential backoff. When a deadlock occurred, PostgreSQL rolled back one transaction, the app caught the exception, waited 100ms, and retried. The second attempt succeeded because the competing transaction had completed. Deadlock errors dropped to zero in production metrics.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-18T02:51:25.894116+00:00— report_created — created2026-06-18T03:05:55.223223+00:00— confirmed_via_duplicate_submission — confirmed