Concurrency control
A retry must repeat the decision, not only the last statement
When a transaction aborts, its earlier reads may no longer be valid. Recompute the protected operation and keep irreversible effects out of the retry block.
In this article
Identify the retry boundary
Consider a transaction that reads a customer's available allowance, calculates a reservation and updates several rows. If it aborts because of a concurrent change, repeating only the final update uses a decision based on old information.
Mark the code that starts a new attempt. It should include the reads and calculations needed to establish the invariant again under the required transaction policy.
Avoid retaining mutable decision state outside that block unless its reuse is explicitly safe. A closure holding the first attempt's balance can quietly defeat a correctly configured transaction retry.
Classify which failures are retryable
A temporary conflict differs from invalid input, denied access or an exhausted business resource. Repeating a permanently invalid operation adds load and delays the useful response.
Use the database's documented error classification and the application's operation policy. Do not retry every exception because some transaction failures are transient.
Set a bounded attempt or time budget. Include the accumulated delay in request timeouts so the caller is not left with an ambiguous result while work continues unnoticed.
Inspect effects outside the database
Look for email delivery, file publication and external API calls inside the attempted transaction. The database cannot roll those effects back when its own work aborts.
Move them into a committed workflow with appropriate delivery and duplicate protection, or use a provider operation identity where the overall design requires it. Document how an unknown external outcome is reconciled.
Do not assume a successful local rollback proves nothing happened elsewhere.
Test a changed decision on retry
Force the first attempt to abort after reading state. Change the relevant data before the next attempt and verify that the new decision reflects it.
For a capacity operation, the retry may now correctly fail because another request consumed the last place. A test that insists every retry succeeds encourages the wrong behaviour.
Also simulate commit success followed by a lost response. This is a different uncertainty from a known aborted transaction and may require idempotent operation recovery. The review should distinguish these cases clearly rather than placing them all under one generic retry helper.
Primary sources
PostgreSQL: transaction isolationAWS: making retries safe with idempotent APIsReferences checked 11 September 2026.