Transactions & Concurrency

Deadlocks, Contention, and Retries

A deadlock is a cycle of waits; ordinary contention is a queue without a cycle. Engines detect cycles or use timeouts and sacrifice work, so applications need short, consistently ordered transactions and bounded, transaction-level retries.
  • A wait-for cycle cannot resolve itself.
  • Timeout is not proof of deadlock.
  • Deadlocks are normal but rates are diagnostic.
  • Retry the whole invalidated unit.
  • Backoff needs jitter and bounds.
  • Idempotency is not automatic.
Two-account deadlock
Start: A=100, B=100; invariant total=200.
T1 locks A, then requests B. T2 locks B, then requests A.
Wait-for edges T1→T2 and T2→T1 form a cycle.
Engine aborts one victim; survivor commits; victim must restart from its first read.
Wait-for graph
Bounded retry policy
deadline = now + 2s; maxAttempts = 4
for attempt in 1..maxAttempts:
  begin; run complete transaction from fresh reads; commit; return
  on classified deadlock/serialization failure:
    rollback; if deadline exceeded: fail
    sleep(random(0, min(25ms * 2^(attempt-1), 250ms)))
Never retry unknown errors or duplicate external effects blindly.
Reduce contention at its source
SymptomPractice
Opposite lock orderSort keys and access tables consistently
Many locksAdd selective indexes; shrink scanned set
Long holdersRemove remote/user waits; commit promptly
Hot rowPartition, batch, or redesign shared counter
Retry stormJitter, cap, admission control, observability