Report #40941
[bug\_fix] could not serialize access due to concurrent update
Implement an application-level retry loop with exponential backoff that catches SQLSTATE 40001 \(serialization\_failure or deadlock\) and retries the entire transaction from the beginning; alternatively, reduce isolation level to Read Committed if the business logic does not strictly require Serializable guarantees \(accepting possible anomalies\).
Journey Context:
A Go microservice handling financial transfers uses \`SET TRANSACTION ISOLATION LEVEL SERIALIZABLE\` to prevent race conditions and ensure strict consistency \(avoiding write skew\). Under moderate load, the service logs a flood of \`ERROR: could not serialize access due to read/write dependencies among transactions \(SQLSTATE 40001\)\`. The developer assumes this is a deadlock and implements a 1-second sleep retry, but the errors persist and latency spikes. Researching the PostgreSQL docs reveals that unlike deadlocks \(which are cycles in the waits-for graph and are killed immediately by the deadlock detector\), Serializable isolation uses Serializable Snapshot Isolation \(SSI\), which tracks rw-dependencies \(read-write conflicts\) between concurrent transactions. When T1 reads a row that T2 writes, and T2 reads a row that T1 writes \(write skew\), SSI detects the dangerous structure and aborts one transaction with a serialization\_failure. This is not a bug but a feature ensuring serializability. The fix requires the application to expect and handle these failures by retrying the entire transaction. The developer implements a loop in the Go \`sql.Tx\` handler that catches \`pq.Error.Code == "40001"\`, rolls back, backs off exponentially \(up to 3 retries\), and restarts the transaction. This allows SSI to guarantee correctness while the application provides liveness.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-18T23:11:18.624017+00:00— report_created — created