ISOLATION LEVELSDOWNTIME PRESSAN INTERACTIVE ESSAY

A field manual for the day every check passed and the rule died

The Skew.

Two transactions. Each sees a perfectly consistent world. Each check passes, each commit succeeds, no error is raised — and together they leave a hospital with no doctor on call. This is write skew, the anomaly that snapshot isolation ships with: The Lock’s dual-write with the network removed, The Lag’s ladder guarding writes. You will walk the crime in step by step, then buy the invariant back four different ways at four different prices.

two private views, one shared truth, and a rule that lives in the gap.

BEGIN
№ 01THE PROMISE

What isolation promised you.

The I in ACID says transactions are isolated — and the picture in your head is honest people taking turns: Alice runs to completion, then Bob. Databases cannot afford that picture. For throughput they interleave constantly — Alice’s read, Bob’s write, Alice’s write, Bob’s commit — and isolation levels are the contract that says which illusions the interleaving may create. The SQL standard offered four floors; the industry quietly standardized a fifth, snapshot isolation, that the standard never heard of.

The victim here is the invariant — a fact that must be true before and after every transaction. The crime formula: each transaction reads a snapshot that satisfies the invariant, each writes a change that preserves it relative to its own snapshot, and the combination breaks it. Note what does not happen: no lock misfires, no network partitions (The Lock is elsewhere), no replica lags (The Lag is elsewhere). One healthy database. Two honest snapshots. This is the crime that needs no accomplice.

№ 02THE SHIFT

Walk them in yourself.

Alice and Bob share the on-call rota; the rule is that at least one doctor must remain. Both would rather go home. Below is their transaction, waiting to happen. Press STEP — each press delivers exactly one protocol event, so you perform the interleaving — or RUN IT to watch the whole schedule play out. Then arm SERIALIZE, RESET, and walk it again to see the guard hold.

FIG. 01 — THE SHIFT · SNAPSHOT ISOLATIONSTEP THROUGH THE FATAL INTERLEAVING · THEN ARM THE GUARD
eight events wait below the fold of one instant. step carefully.

Go back and read the fourth step slowly, because it is the whole essay: Bob’s check passes, and the check is honest. His snapshot genuinely contains two doctors. Alice’s snapshot was genuine too. No component lied — the snapshots were consistent, the writes were legal, the commits were orderly. The invariant died in the gap between two truths, which is a place no single transaction can see and no single-transaction test will ever cover.

And notice what the database never did: hold a wrong state. Committed reality went 2 → 1 → 0 with every intermediate value perfectly legal under the rule as each writer saw it. Monitoring showed a healthy database. The roster showed an empty shift. Both were right about their own layer.

MODEL NOTES — snapshot isolation per Postgres REPEATABLE READ · first-committer-wins on write-write overlap · the two updates touch different roster entries, so no write conflict exists · SSI behavior per Cahill 2008 / Ports–Grittner 2012, compressed to one abort.

№ 03THE PATTERN

One crime, many suits.

The shift is one costume on a single skeleton: T1 and T2 read an overlapping set; each writes a disjoint set; each write invalidates the other’s premise. Try the skeleton on for size:

The meeting room: two admins check “is 3 p.m. free?” — both see yes, both book, the room is double-booked. The maintenance flag: two workers both see no active claim, then write separate claim rows and each runs a once-only task. The accounts: the rule is “savings + checking ≥ $0”; one transaction withdraws from savings, another from checking, each sees the combined balance as healthy, together they go negative. The names change — double-booking, duplicate task, overdraft — the skeleton is always write skew.

Now the deep question: why didn’t the database stop it? Snapshot isolation arbitrates conflicts by comparing write sets — first-committer-wins: if two transactions wrote the same row, the later commit aborts. But Alice wrote Alice’s roster entry and Bob wrote Bob’s. Disjoint writes, no collision, both pass. The dangerous dependency was read-write: each transaction read the very thing the other’s commit would invalidate. Two read-write antidependencies form a cycle — and a cycle of reads is invisible to a guard that only watches writes.

Snapshot isolation is almost serializable — and “almost” is exactly one row wide.THE FINE PRINT, PER BERENSON ET AL. AND ADYA’S ANOMALY TAXONOMY
№ 04THE LADDER

What each floor permits.

The Lag climbed a ladder of read guarantees; this is its twin for transactions. Click any row — the anomalies a level permits are the holes in its floor. One row is outlined: find it before you click it.

FIG. 02 — THE LADDER · WHO PERMITS WHAT✓ PREVENTED · ✗ POSSIBLE · CLICK A ROW
Pick a row. Each anomaly is a different way two honest transactions combine into one dishonest world.

The outlined row is the essay. Write skew survives every level except SERIALIZABLE — including snapshot isolation, the modern default that Postgres ships as REPEATABLE READ and most teams treat as “safe.” It survives because the standard’s four levels were written for a locking world in the 1970s; MVCC arrived later with a genuinely new contract and, buried in its fine print, one new way to be wrong. The Conf loaded that fine print: “snapshot isolation is almost serializable.” In an interview, knowing the name and shape of that one exception is a stronger signal than reciting all four levels.

WHY THE CYCLE IS INVISIBLE — DEPENDENCIES IN ONE PARAGRAPH

Draw each transaction as a dot. A wr edge runs from a writer to a transaction that reads its value; a ww edge orders two writes to one version chain. An rw antidependency runs from a transaction that read an old version to another that replaced it. A cycle means the execution is not serializable.

Snapshot isolation’s first-committer-wins rule rejects concurrent writes to the same row; it does not erase every ww dependency. Here the writes are disjoint. Alice read Bob’s old on-call row before Bob removed it, giving Alice →rw Bob. Bob read Alice’s old row before Alice removed it, giving Bob →rw Alice. Those two edges close the cycle.

Serializable Snapshot Isolation (SSI) tracks dangerous rw patterns and aborts a transaction before a nonserializable history commits. The price is retrying serialization failures, including some conservative aborts.

№ 05THE SENTENCE

Four ways to buy it back.

The invariant is worth protecting; the question is which bill you sign. Click a sentence — each replays the shift under a different guard and shows its price.

FIG. 03 — THE SENTENCE · SAME CRIME, FOUR FIXESCLICK A CARD · READ THE OUTCOME AND THE INVOICE
no fix selected — the crime stands as committed.

One humility note to carry home: “we use transactions” is not an invariant story. The hospital had transactions — good ones, at a respectable level — and still ran an empty shift. The invariant lives in the gap unless something you deployed owns it explicitly: a constraint, a lock, a counter-row, or a serializable check. Name which one owns each of your invariants, and you are ahead of most systems running today.

№ 06FIELD GUIDE & PROOF

The field guide.

Isolation level
The contract for what interleaving may blur. RC is the default nearly everywhere; snapshot isolation is the modern upgrade; SERIALIZABLE is the only floor without holes.
Snapshot isolation
One consistent point-in-time view per transaction; writes arbitrated by first-committer-wins on overlapping rows. Fast, honest, and — see FIG. 01 — one anomaly short of safe.
Write skew
Two transactions read an overlap, write disjoint sets, and each invalidates the other’s premise. The constraint of “≥1” died between two true snapshots. Survives SI; only SERIALIZABLE (or your own guard) stops it.
First-committer-wins
SI’s write guard: if two transactions wrote the same row, the later committer aborts. Blind to read-write cycles — which is exactly where write skew lives.
RW-antidependency
A reader sees an old version, then another transaction replaces it: the edge runs from that reader to the later writer. The hospital’s two crossing edges create a cycle.
SSI
Serializable Snapshot Isolation — Postgres 9.1+, CockroachDB’s default. Tracks rw edges, aborts when a cycle threatens. Pays in retries to avoid paying in dead invariants.
Materialized conflict
Turning an implicit invariant into explicit data — a counter row both transactions must write — so the existing write-write guard can see the collision.
Serialization failure
The abort SSI hands you. Not an error: a ticket. Retry the transaction; the retry sees committed truth and decides correctly. (The Ack, institutionalized: the caller must retry a transaction after the database aborts it.)

Three scenarios.

From these figures, you can show why two locally valid snapshot transactions can violate a shared invariant. Try changing one assumption and check whether your explanation still holds.

The sealed sheetThree questions are sealed inside this sheet. Nobody is asked to open it — the rota was fine in your snapshot.Break the seal
question 1 of 3

FIG. 01, replayed: both doctors checked “2 ≥ 2 ✓” and both left; the roster is empty. What is this anomaly, and which level permitted it?