Serializable Snapshot Isolation

Topics Covered

What Snapshot Isolation Misses

The gap in one sentence

Why the mechanism cannot see it

Why the shape is common in applications

Why it is not a lost update

What the obvious remedies cost

Why two-phase locking is the pessimistic answer

What the second anomaly is

Why a read-only transaction can be the odd one out

Why the anomalies are rare in practice

Why rarity makes them worse

What a correct mechanism must do

Why that requires tracking reads

Why the tracking must not block

Why detecting is cheaper than preventing

Why the mechanism must sometimes abort

Why the abort is not a deadlock

Why this mechanism was adopted so widely

What to take from the gap

Snapshot isolation is nearly serializable, and the gap between nearly and actually is one specific shape that turns out to be both common in applications and cheap to detect.

The gap in one sentence

A transaction can read a set of rows, decide something from them, and write a row that invalidates the decision another transaction made from the same set, with neither write touching what the other read.

Stating the gap before naming it keeps the focus on mechanism. The two transactions never write the same row, so no write-conflict check can see them, and each sees a perfectly consistent snapshot.

Why the mechanism cannot see it

Versioning compares versions of individual items, and the conflict here is between a read in one transaction and a write in another, which leaves no trace on any version.

Explaining the blindness precisely is the whole motivation for the lesson. Nothing recorded by the mechanism so far mentions what a transaction read, so there is nothing to compare.

Each transaction reads the same pair, each checks a condition that holds, and each writes the other one. No write meets another write, and the invariant is gone.

Why the shape is common in applications

Reading something to decide whether an action is permitted and then performing the action is the ordinary structure of business logic, and the thing read is usually not the thing written.

Pointing at the ubiquity matters because the anomaly sounds exotic. Checking a balance before a withdrawal, checking capacity before a booking and checking a count before a removal are all the same shape.

Why it is not a lost update

Nothing is overwritten. Both writes survive, and the problem is that the combination of them violates something each transaction individually verified.

Distinguishing the two is necessary because the remedies differ. A lost update is prevented by a check on a single item, and this one cannot be, which is why it needed an entirely different mechanism.

What the obvious remedies cost

Taking write-strength claims on everything read serialises every read-then-write pair including the overwhelming majority that would never have conflicted.

Being concrete about the cost explains why something better was wanted. The application-level remedy works and is pessimistic, and a mechanism that waits until it knows a conflict occurred can be far less costly.

Why two-phase locking is the pessimistic answer

Holding shared claims to commit and locking ranges prevents the anomaly by construction, at the price of blocking readers against writers again, which is what versioning existed to avoid.

Making the comparison explicit frames the design space. The question that produced this lesson's mechanism was whether serializability could be had while keeping the property that readers never block.

What the second anomaly is

A read-only transaction can observe a state that no serial order could produce, even though it writes nothing and therefore cannot itself be the cause of any conflict.

Covering this case matters because it contradicts an intuition people hold firmly. The belief that a read-only transaction is always safe is wrong under snapshot isolation, and that fact surprises experienced engineers.

Why a read-only transaction can be the odd one out

Its snapshot can straddle two transactions in a way that makes their effects appear in an order inconsistent with what another observer would see.

Explaining the shape briefly is enough for this lesson. The important consequence is that a mechanism aiming at serializability cannot simply exempt read-only transactions from its checks, even though exempting them would be convenient.

Why the anomalies are rare in practice

They require a particular overlap in time between particular transactions, so an application can run for years with the latent bug and never produce an incorrect result.

Noting the rarity is important for calibration. The failure mode is an occasional wrong answer with no error and no log entry, which makes it one of the hardest classes of bug to attribute after the fact.

Why rarity makes them worse

A bug that fires under load once a month is harder to find, harder to reproduce and more likely to be blamed on something else than one that fires every time.

Drawing out the consequence justifies the effort the mechanism represents. The whole point of detecting these cases automatically is that human review will not find them reliably.

What a correct mechanism must do

Notice when one transaction reads something another transaction then writes, and decide whether the resulting pattern of dependencies could have arisen from some serial order.

Framing the requirement as a question about dependencies prepares the next section. The mechanism needs a way to talk about the relationships between transactions rather than about the values of items.

Why that requires tracking reads

A write leaves a version behind and a read leaves nothing, so the engine must record what each transaction read in order to notice a later write touching it.

Identifying read tracking as the new cost is the honest framing. Everything expensive about the mechanism in this lesson follows from having to remember reads, which the engine previously did not.

Why the tracking must not block

If a recorded read caused a later writer to wait, the mechanism would be two-phase locking again, so the record has to be informational rather than exclusionary.

This is the design constraint that makes the mechanism interesting. The structure looks like a lock and behaves like a note, which is why descriptions of it that use the word lock are so easy to misread.

Why detecting is cheaper than preventing

Prevention must act on every case that could conflict, and detection acts only on cases that did, which on a workload with few real conflicts is a very large difference.

Stating the general principle is worth doing because it applies well beyond this mechanism. Optimistic approaches win exactly when conflicts are rare, and the question is always whether a given workload meets that condition.

Why the mechanism must sometimes abort

Having let the work proceed, the only remedy available when a conflict is found is to undo one of the transactions, so the application must be prepared for a failure it did not cause.

Being clear about the obligation early sets expectations. The application cost of this mechanism is retry logic, which is the same cost snapshot isolation already imposed, now with a larger set of reasons.

Why the abort is not a deadlock

Nothing is waiting for anything, and the abort arrives because a pattern was observed rather than because progress became impossible.

Separating the two is worth a sentence because they present similarly. Both arrive as a retryable error, and their causes and remedies are entirely different, so conflating them leads to the wrong investigation.

Why this mechanism was adopted so widely

It provides genuine serializability while preserving the property that readers never block writers, which no other mechanism manages.

Saying so plainly is the summary of the motivation. It is the only option that does not require giving up either correctness or the main benefit of versioning, and that is a strong position.

What to take from the gap

Three things. Snapshot isolation's gap is one shape: a transaction reads a set, decides, and writes a row that invalidates a decision another made from the same set, with neither write touching what the other read, so no write-conflict check can see it. The shape is the ordinary structure of business logic rather than an exotic case, and its rarity in time makes it harder to find rather than less important. And closing the gap requires recording what each transaction read, which is the new cost, and that record must inform rather than block or the mechanism becomes locking again.

Key Insight

A read-only transaction can observe a state no serial order could produce, even though it writes nothing. The intuition that a transaction which cannot change anything cannot be involved in an anomaly is wrong under snapshot isolation, and it is held firmly by people who otherwise reason carefully about concurrency. It is also the reason a serializable mechanism cannot simply exempt read-only transactions from its checks, however convenient that would be.