shreyansh

Serializable Snapshot Isolation

30-Second Glance

  • SSI = snapshot isolation + abort on "dangerous structures": optimistic serializability where readers never block.
  • Tracks read-write antidependencies (T1 reads what concurrent T2 wrote); a 2-cycle of them means a serial order is impossible → abort one side.
  • PostgreSQL's SERIALIZABLE is SSI: same snapshot semantics as repeatable read, plus predicate-aware SSI checks (SIREAD locks, no blocking).
  • Best for low-contention workloads with strict invariants; under hot-row storms abort rates spike — then prefer 2PL or explicit locks.

From Snapshot to Serializable

Snapshot Isolation fails exactly on predicate anomalies (Write Skew and Phantom Writes): disjoint writes, invisible phantoms. SSI (Cahill–Röhm–Fekete, Postgres 9.1+) closes the gap without taking blocking locks. Each transaction runs on its snapshot as usual, while the engine records:

  • rw-antidependency T1 --rw--> T2: T1 read a version that T2 (concurrent, overlapping lifetime) later overwrote — T1 appears "before" T2 but read stale data relative to T2's write.
  • Dangerous structure: T1 --rw--> T2 --rw--> T3 with a commit-order violation — in the simplified interview form, a cycle of rw-antidependencies among concurrent transactions. No serial order can satisfy all of them, so one transaction must abort (Postgres 40001).

Crucially these are SIREAD (predicate) locks: they record what was read (including gaps/ranges, hence phantom detection) but never block anyone. They only cause aborts at commit time. Readers keep full MVCC non-blocking behavior — the headline advantage over Two-Phase Locking.

loading diagram…

The On-Call Case Under SSI

Revisit the rota: T1 reads {Alice, Bob}, writes Alice; T2 reads {Alice, Bob}, writes Bob — concurrently. T1's read of Bob's row is antidependent on T2's write of Bob; T2's read of Alice's row is antidependent on T1's write of Alice. Cycle → whoever tries to commit second aborts with could not serialize access due to read/write dependencies. The survivor's decision was made on a snapshot the other concurrently invalidated — exactly right to retry.

False positives exist: the detector is conservative (it aborts some executions that were actually serializable) to keep tracking cheap. Measured SSI abort overhead on uncontended TPC-C is ~1–5%; pathological hot-spot workloads can see 10–30%+ abort rates, at which point pessimistic locking or partitioning wins.

Concrete Numbers

Quantity Typical value Why it matters
SSI tracking cost In-memory rw-graph + SIREAD entries until overlapping txns all commit Short txns keep it trivial; long readers pin SSI state like snapshots pin VACUUM
Abort rate, low contention <1–5% Retry-with-backoff is invisible
Abort rate, hot spot 10–50%+ Signal to switch strategy (lock the hot row, partition, batch)
Safe-retry snapshot Postgres DEFERRABLE serializable takes a safe snapshot (may wait) Read-only analytics get zero-abort serializable snapshots at the cost of initial wait
Where available PostgreSQL SERIALIZABLE, FoundationDB, CockroachDB (variant) InnoDB SERIALIZABLE is 2PL-style locking instead — engine matters

SSI vs 2PL: Choosing

Dimension SSI (optimistic) 2PL (pessimistic)
Reader cost Zero blocking, snapshot reads S locks block writers; p99 suffers under contention
Failure mode Aborts (40001) concentrated at commit Waits/deadlocks spread through execution
Hot spots Abort storms Lock queues + collapse
Predicate handling SIREAD tracking, automatic Gap/next-key locks, index-dependent
Best workload Read-heavy + occasional strict writes; PG shops Write-contended invariants; legacy RDBMS; when retries are unacceptable

Interview line: "SSI when aborts are cheaper than waits (low contention, read-heavy); 2PL when waits are cheaper than aborts (known hot rows, short strict txns)."

Trade-offs

Approach Pros Cons Use when
SSI serializable True serializability; non-blocking reads; automatic predicate coverage Abort/retry logic mandatory; false positives; SSI state with long txns Postgres OLTP with roster/booking/money invariants at normal contention
2PL serializable No abort storms; deterministic blocking Deadlocks; p99 latency; gap-lock complexity Hot-row strict workloads, InnoDB shops
Snapshot + constraints Cheapest when invariants are constraint-shaped Leaves general predicates exposed Uniqueness/range invariants only
Serial execution No anomalies, no aborts Partition/sproc discipline Partitionable high-TPS OLTP

Interview One-Liners

  • Q: SSI in one sentence? A: Optimistic serializability: run on snapshots, track read-write antidependencies including predicates, abort on cycles — readers never block.
  • Q: What is an rw-antidependency? A: T1 read data that concurrent T2 overwrote — T1's decision rested on state T2 invalidated, so ordering T1 before T2 is suspect.
  • Q: What triggers the abort? A: A dangerous structure (cycle of rw-antidependencies among overlapping txns) proving no equivalent serial order exists.
  • Q: What are SIREAD locks — do they block? A: Non-blocking markers recording what (including gaps) a txn read; they exist only to detect phantoms and predicate cycles at commit.
  • Q: Postgres REPEATABLE READ vs SERIALIZABLE? A: Same snapshot mechanics; SERIALIZABLE adds SSI tracking + dangerous-structure aborts. Both need 40001 retry handling.
  • Q: When does SSI lose to 2PL? A: Hot-spot write storms where optimistic aborts (10–50%+) cost more than pessimistic waiting — lock the hot domain instead.