4.4.20 · D4Databases

Exercises — Optimistic vs pessimistic concurrency control

3,243 words15 min readBack to topic

Deriving the optimistic cost — where does come from?

We build the formula from a picture rather than dropping it on you.

The figure below draws that ladder so you can see each step shrinking — the total area of the bars is exactly the sum above.

Figure — Optimistic vs pessimistic concurrency control

Reading the ladder figure (walk-through). Each pink bar is one rung of the ladder: the first rung has height (probability a first retry is needed), the second has height , the third , and so on — each rung a fraction shorter than the one before, so they shrink toward zero. Stack (add) all the bar heights and you get , marked by the yellow bracket. Multiply the whole stack by the cost-per-retry and you have . The picture makes the "geometric" nature visible: the bars form a shrinking staircase, never quite ending but summing to a finite total whenever .

Figure — Optimistic vs pessimistic concurrency control

Reading the cost-curve figure (walk-through). The horizontal axis is , the conflict probability, running . The vertical axis is expected overhead per transaction in milliseconds. The blue flat line is — it never moves because a lock is paid no matter what is. The pink rising curve is with : it starts at when , stays low while conflicts are rare, then bends upward and rockets toward infinity as (the "cost explodes" arrow). They cross at the yellow dashed line ; left of it the pink curve is below the blue line (optimistic cheaper), right of it pink is above (pessimistic cheaper). That single crossing point is the whole decision.


Level 1 — Recognition

Recall Solution 1.1

Pessimistic. The clue is FOR UPDATE, which acquires an exclusive (X) lock on the selected rows before any work is done — the transaction assumes a conflict will happen and locks first. The mechanism is locking, typically under Two-Phase Locking (2PL).

Recall Solution 1.2

Optimistic. No lock is taken during read/compute. The WHERE version = 7 is the validation step: if another transaction bumped the version first, this update matches 0 rows and we know to retry. This is the version-check flavour of OCC.

Recall Solution 1.3

Write skew. Two transactions read overlapping data but write to different rows, so a plain single-row lock or single-row version check does not catch it. See Write Skew and Phantoms.


Level 2 — Application

Recall Solution 2.1

Meaning: if fewer than of transactions conflict, optimistic is cheaper on average.

Recall Solution 2.2

, so optimistic wins. Sanity check: , exactly as the threshold predicts.

Recall Solution 2.3

, so pessimistic wins. And , consistent with the threshold.


Level 3 — Analysis

Recall Solution 3.1

That is 76× the pessimistic cost of ms. Why livelock: almost every transaction fails validation and retries, its retry fails again, and the useful lock-free work is thrown away over and over — the system spins without making progress. Use a lock, a queue, or an atomic decrement instead.

Recall Solution 3.2

No deadlock. OCC takes no long-held locks, so there is nothing to wait on. Both transactions do their work on private copies. At commit, at most one validates cleanly; the loser sees a version mismatch and aborts and retries. OCC trades waiting (deadlock risk, see Deadlocks) for retrying (livelock risk). Only one failure mode at a time.

Recall Solution 3.3

No, it does not. Each transaction updates a different row (its own doctor), so both version checks succeed independently — neither row was touched by the other transaction. The conflict lives in a predicate ("count of on-call doctors ≥ 1") spanning multiple rows, not in any single cell. You need predicate/range locking or serializable isolation (Serializability and Isolation Levels, Write Skew and Phantoms).


Level 4 — Synthesis

Recall Solution 4.1

Use an atomic increment / Compare-and-Swap (CAS) loop, or UPDATE ... SET likes = likes + 1. The row is a single hot cell so is high, which would make a general OCC retry-then-rerun-all-work loop expensive. But CAS's "retry" is just a re-read + re-compare of one integer, whereas general OCC's retry re-runs the whole transaction's read/compute/I/O. Concrete comparison. Suppose a general OCC transaction re-runs ms of work per retry, while a CAS spin costs ms (a couple of CPU instructions). Even at a brutal : Same multiplier, but because the CAS scheme is 160× cheaper. That is why atomic ops thrive on hot counters where general OCC dies. If contention is truly extreme, shard the counter per-thread and sum on read.

Recall Solution 4.2

Threshold .

  • Reports, : ms vs ms → optimistic (MVCC snapshot reads). .
  • Inventory, : ms vs ms → pessimistic (SELECT ... FOR UPDATE). .

Same database, opposite strategies — chosen per-workload by comparing to .

Recall Solution 4.3

Old . New . The threshold rises from to , so a wider band of workloads (those with ) now flip to favouring optimistic. Cheaper retries push the boundary right, making the optimistic region larger. What this means for your workload mix: any workload whose conflict rate sits in that newly-captured band — previously assigned to pessimistic locking — is now cheaper under optimistic control and should be migrated. In practice, investing engineering effort to make retries cheaper pays off twice: it lowers the cost of the retries you already do, and it enlarges the set of workloads that qualify for the lock-free (higher-throughput) path.


Level 5 — Mastery

Recall Solution 5.1

Not necessarily true. MVCC is a storage technique — keep multiple versions so readers see a consistent snapshot and don't block writers. It is orthogonal to the conflict-handling philosophy.

  • MVCC + pessimistic writes: PostgreSQL's default — readers use MVCC snapshots, but writers still take row locks (e.g. SELECT ... FOR UPDATE, UPDATE).
  • MVCC + optimistic validation: Serializable Snapshot Isolation (SSI) layers an optimistic conflict-check on top of MVCC and aborts offenders at commit. Conclusion: MVCC ≠ OCC. It can serve either.
Recall Solution 5.2

Set with : cancel to , so , giving . Interpretation: when a lock costs exactly as much as a retry, the fair coin-flip point is conflict. Below half the transactions conflicting, optimistic; above, pessimistic. This is the only case where the popular " rule of thumb" is actually correct — it silently assumes .

Recall Solution 5.3

Why it diverges: the expected number of retries is ; a retry only succeeds with probability , and as the expected count of attempts before a success grows without bound — the transaction can never "get through the door." Pessimistic contrast: is a constant, independent of . Locking serializes the crowd so each transaction waits its finite turn and then succeeds once. This flat-versus-explodes divergence (visible as the right edge of the s02 figure) is the deepest reason to prefer locks under heavy contention.

Recall One-line summary to lock in

Pessimistic cost is a flat line ; optimistic cost is a curve that starts near and explodes at . They cross at — everything on this page is a consequence of that one picture.