Skip to content

Concurrency control

Making read-decide-write sequences safe when other actors may change the same data in between: locks, conditional writes and constraints.

Concurrency

Learn it

0 of 3 checks done
  1. A lot of code reads something, makes a decision, then writes: "if the seat is free, book it". Each step is fine on its own. The trouble starts when two requests run at the same time and their steps interleave:

    Request A                     Request B
    SELECT seat 12  → free
                                  SELECT seat 12  → free
    UPDATE seat 12 owner = A
                                  UPDATE seat 12 owner = B
  2. Check

    After that sequence, who has seat 12, and who was told they booked it?

Quick reference

The same ideas, condensed for revision.

How it goes wrong

Check-then-act
Read and write are separate statements with nothing preventing interleaving.
Lost update
Two writers read, modify and write back full rows; the second silently overwrites the first.
Deadlock
Two transactions lock rows in opposite orders and wait on each other forever (until the database kills one).
Locks held across network calls
A slow dependency turns every waiter into a timeout.

Instead, consider

Single writer
Route all writes for a key to one process or thread, so concurrent access cannot happen. See [[partitioning]].
Commutative operations
The operations can be applied in any order with the same result (increments, CRDTs).
Serializable isolation
Invariants span many rows and you prefer the database to detect conflicts and abort.

In practice

SELECT … FOR UPDATE / SKIP LOCKED
Row locks; SKIP LOCKED lets job claimers bypass rows being claimed.
Version columns / ETags
Optimistic concurrency with conditional writes.
Unique and check constraints
Invariants enforced by the database for every writer.
Compare-and-set in key-value stores
Redis WATCH, DynamoDB condition expressions, etcd transactions.

It assumes

  • All writers go through the same mechanism; one unguarded path breaks the invariant.
  • Conflict handling (retry, reject, merge) is defined for the losing side.

Explain it in your own words

Write at least 60 characters (0 so far). Write it as you would say it in a design review. You will compare it against the points a strong answer makes.

Where you practise it

Further reading

Engineers describing it in systems they run.

  • How Convex Works

    Convex · Sujay Jayakar · Post, Apr 2024

    A walk through a reactive database from the inside: the transaction log, read sets, optimistic concurrency, and how a write finds the subscriptions it affects.

  • Transactions

    Grouping several reads and writes so they take effect all together or not at all, isolated from concurrent work to a defined degree.

  • State machines for business state

    Modelling an entity's lifecycle as explicit states and allowed transitions, enforced with conditional updates so concurrent or stale actors cannot corrupt it.

  • Leases and fencing tokens

    Ownership that expires unless renewed, plus a token that lets the rest of the system reject an owner that has lost its claim without knowing it.

  • Idempotency

    Designing an operation so that performing it twice has the same effect as performing it once, which is what makes retries safe.

  • Generating unique identifiers

    Making ids that are unique across machines and time, and choosing what else they reveal: order, volume, guessability, length.