Contents

Programming Fundamentals › Concurrency & Async · also in Transactions & Concurrency Control

Race Condition

A bug where the result depends on unpredictable timing.

Also known as: race conditions, data race, race bug, timing bug, concurrency bug

A race condition is a bug where the result depends on the timing of events that you don’t control, such as which of two threads, requests or processes gets there first. Run it a hundred times, and it works ninety-nine.

The classic example: two requests increment a counter.

count = db.get("visits")        # both read 10
count = count + 1               # both compute 11
db.set("visits", count)         # both write 11  → one increment is lost

Expected 12, got 11. Each step is fine on its own, but the interleaving loses an update (read-modify-write, lost update).

Other typical forms:

  • Check-then-act: “if the seat is free, book it”. Two users pass the check, and both book it (check-then-act).
  • Double submission creating duplicates (double submit).
  • Out-of-order responses in a UI: a slow request for an old search finishes after the new one and overwrites the results.
  • Initialization: two threads both see “not created yet” and create the resource.

Why they’re nasty

They’re non-deterministic: they appear under load, on certain machines, or once a month, and disappear when you add a log line or attach a debugger (a “Heisenbug”, see Heisenbugs). Tests pass.

How to prevent them

TechniqueIdea
Don’t share mutable stateImmutable data, per-request objects, message passing
Make the operation atomicA single statement: UPDATE visits SET n = n + 1 (atomic update), atomic operations (atomic operation)
LocksLet one at a time in (mutex), or database locks (pessimistic locking)
Optimistic concurrencyDetect the conflict and retry (optimistic locking)
Let the database reject itA unique constraint or transaction (unique constraint guard)
Idempotency keysMake repeats harmless (idempotency key)

Habits

  • Whenever code reads, decides and then writes, ask: what if someone else does this between my read and my write?
  • Assume that multiple instances of your service run at once. In-process locks don’t protect across machines.
  • Stress test with concurrency, and review shared state (thread safety).
  • On the frontend, cancel or ignore stale requests.