Knowledge Guide
HomeSystem DesignScalable Systems (Advanced Topics)

hard Write Skew, Lost Update & Snapshot Isolation

Anomalies the ANSI triad never mentions

The textbook isolation ladder is defined by three anomalies it forbids — dirty read, non-repeatable read, phantom. But two of the most dangerous anomalies are not on that list: lost update and write skew. Both survive under snapshot isolation (SI), the level most people are actually running when they select “REPEATABLE READ” in PostgreSQL (Oracle has no REPEATABLE READ level — it exposes the same snapshot isolation under the name “SERIALIZABLE”). The mechanism: SI gives each transaction a consistent snapshot as of its start and lets readers never block writers — but a decision made against a stale-but-consistent snapshot can be wrong by the time it commits.

Two transactions read the same snapshot showing both doctors on call, each takes one doctor off call on a different row, both commit, leaving zero on call
Two transactions read the same snapshot showing both doctors on call, each takes one doctor off call on a different row, both commit, leaving zero on call

Worked example: the on-call doctors (write skew)

Invariant: at least one doctor must be on call. Alice and Bob are both on call and both feel unwell.

stepT1 (Alice)T2 (Bob)
1SELECT count(*) WHERE on_call2
2SELECT count(*) WHERE on_call2 (same snapshot)
32 ≥ 1, safe to leave → UPDATE alice SET on_call=false
42 ≥ 1, safe to leave → UPDATE bob SET on_call=false
5COMMITCOMMIT

Both commit. Now zero doctors are on call. Each transaction read a snapshot where the invariant held, and each wrote a different row — so there is no write-write conflict for SI to detect. This is write skew: the writes don’t overlap, but the premise each read is invalidated by the other’s write.

Its simpler cousin: lost update (same row)

Two read-modify-write cycles on the same row. balance = 100:

T1: read balance (100)         T2: read balance (100)
T1: balance = 100 + 50         T2: balance = 100 + 20
T1: write 150                  T2: write 120   // overwrites 150; +50 is lost

The correct result is 170; you get 120. The naive balance = balance + x read-then-write is the bug — an atomic UPDATE ... SET balance = balance + 50 (a single statement) avoids it. Crucially, unlike write skew, lost update touches the same row, so it is a write-write conflict — and SI in PostgreSQL/Oracle detects it and aborts the second committer (first-committer-wins). Plain READ COMMITTED does not.

The correctness fact people get wrong

PostgreSQL’s “REPEATABLE READ” is really SNAPSHOT ISOLATION, which permits write skew. Oracle has no REPEATABLE READ level at all — it exposes SI under the name “SERIALIZABLE”, and, crucially, Oracle’s “SERIALIZABLE” is not truly serializable: it still permits write skew. Only PostgreSQL’s SERIALIZABLE, backed by SSI (Serializable Snapshot Isolation) — which tracks read/write dependencies and aborts a transaction that would complete a dangerous cycle — actually prevents it.

So the doctors bug is invisible under SI (PostgreSQL REPEATABLE READ / Oracle “SERIALIZABLE”) and caught only by PostgreSQL’s SSI-backed SERIALIZABLE (one transaction aborts with a serialization failure; the app retries).

MVCC visibility — the detail to keep straight

Do not over-generalize “snapshot at transaction start” to READ COMMITTED — under READ COMMITTED the snapshot advances with every statement, which is exactly why a second read can return a newer value.

Pitfalls

The judgment layer

Snapshot Isolation vs Serializable. SI is fast — readers and writers never block each other — and correct for the vast majority of transactions; its blind spot is multi-row invariants (write skew). SERIALIZABLE (SSI) closes that hole but pays for it with extra bookkeeping and, under contention, serialization-failure aborts that the application must catch and retry. Choose SERIALIZABLE when an invariant spans rows and correctness is non-negotiable (scheduling, double-spend, inventory); stay on SI where transactions are independent and throughput matters.

Named alternatives short of full serializable. SELECT ... FOR UPDATE explicitly locks the exact rows the invariant reads, turning the silent read into a real write-lock conflict — cheap and surgical when the rows already exist. When they don’t (phantoms), materialize the conflict: insert/lock a dedicated row representing the constraint (e.g. one row per on-call shift) so contenders serialize on it. These target one query; SERIALIZABLE protects the whole transaction blanket-wise at higher cost.

Takeaways


Synthesized from Kleppmann, Designing Data-Intensive Applications (Ch. 7), Berenson et al. “A Critique of ANSI SQL Isolation Levels,” and the PostgreSQL documentation on transaction isolation and SSI (Ports & Grittner). Re-authored/Deepened for this guide.

🤖 Don't fully get this? Learn it with Claude

Stuck on Write Skew, Lost Update & Snapshot Isolation? Open Claude, copy a block below, and it'll teach you this exact concept — visually and interactively.

🎨 Explain it visually

Build the mental picture, not memorization.

I just read a lesson on **Write Skew, Lost Update & Snapshot Isolation** (System Design) and want to truly understand it. Explain Write Skew, Lost Update & Snapshot Isolation from first principles using ONE vivid real-world analogy and a visual mental model — draw it as ASCII art or a clear step-by-step diagram — with a concrete example using real numbers. Then ask me one question to check I got the mental picture, and wait for my reply. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🤔 Walk me through it (interactive)

Socratic — adapts to where you're stuck.

Teach me **Write Skew, Lost Update & Snapshot Isolation** interactively. Ask me ONE guiding question at a time, wait for my answer, and adapt to my confusion — build the idea with me step by step instead of explaining it all at once. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🧪 Quiz me & fix my gaps

Active recall exposes what you missed.

Quiz me on **Write Skew, Lost Update & Snapshot Isolation** with 5 questions, easy to tricky, ONE at a time. Tell me if each answer is right; at the end, explain clearly what I got wrong and why. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🧠 Make it stick

Intuition + hook + flashcards for long-term memory.

Help me remember **Write Skew, Lost Update & Snapshot Isolation** for the long term: give the one-sentence intuition, a memorable hook/mnemonic, a tiny worked example, and 3 active-recall flashcards (Q -> A). If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.

📝 My notes