Failures
Failures
GitHub
Home Docs Concurrency
HIGH

Concurrency

"What if two actors modify the same state?"

Invariant: Concurrent modifications must not cause lost updates or double effects.

Applies To

inventory, balances, enrollments, token refresh, counters

Why It Happens

A first-principles walkthrough of what happens when two actors read the same row at the same instant. The lost-update — both read seats=1, both write 0, one sale disappears. Why read-modify-write is never safe without a lock, and how SELECT FOR UPDATE and versioned optimistic locking make the check atomic at the database, not in application memory.

How It Works Underneath

Concurrency is two actors reading the same row at the same instant. Each reads seats=1, each checks if seats>0, each writes seats=0. One sale consumed two seats, one customer is oversold, and no error was thrown. The database executed both writes correctly — the bug is that the check and the write were not atomic. The fix is to make the check part of the write: UPDATE seats SET seats=seats-1 WHERE seats>0 and inspect rows_affected, or SELECT ... FOR UPDATE to lock the row between read and write, or a version column with WHERE version=expected.

T1: READ seats=1 ─┐
T2: READ seats=1 ─┼─ both see 1, both write 0 → lost update
Fix: UPDATE ... WHERE seats>0 → second UPDATE affects 0 rows → 409

Common Misconceptions

“My language is single-threaded, so no race.” Concurrency is not threads — it is two HTTP requests from two users hitting two server processes at the same millisecond. The race is in the database, not in your runtime.

Cataloged Failure Modes

Code Comparison

Language:
Fragile (AI Happy Path) — Python concurrency_fragile.py
# NAIVE: Read-modify-write race condition
async def purchase_ticket(event_id: str):
    event = await db.fetch_one("SELECT seats_left FROM events WHERE id = %s", event_id)
    if event['seats_left'] > 0:
        await db.execute("UPDATE events SET seats_left = %s WHERE id = %s", event['seats_left'] - 1, event_id)
        return True
    return False
Resilient (Failures Verified) — Python
concurrency_safe.py
# IMPROVED: Optimistic locking with atomic condition
async def purchase_ticket(event_id: str):
    res = await db.execute("UPDATE events SET seats_left = seats_left - 1, version = version + 1 WHERE id = %s AND seats_left > 0", event_id)
    if res.rows_affected == 0:
        raise HTTPException(409, "No seats available or concurrent conflict")
    return {"status": "confirmed"}

Mitigation Patterns