Section 9: Two Writers, One Key - Deep Dive: Conflicts, Clocks & Anti-Entropy
Executive Summary: This section confronts the bill that leaderless replication deferred: two coordinators can both legally accept writes to the same key, and the replicas end up disagreeing. We resolve the conflict with last-write-wins on a (timestamp, node-id) pair and name exactly what it loses; we face the clock-skew lie inside the word "last"; we teach vector clocks fairly, including why Dynamo's own heirs walked away from them; and we close the repair trio — hinted handoff, read repair, Merkle anti-entropy — plus the tombstone-and-grace-period discipline that keeps deleted data dead.
Section 8 ended with a question the storage engine cannot answer. Checkout's user taps "add gift wrap" on their phone while their laptop tab, still open, retries a stale save. Two app servers issue put(cart:8842) within the same two milliseconds, through two different coordinators — remember from Section 7 that any node can coordinate. Both writes reach W=2 of the N=3 replicas. Both clients get success. Both successes are honest. And now replica n09 holds the blue-wrapped cart, n23 holds the green, and n17 — depending on timing — holds either. No node crashed. No packet dropped. The system worked exactly as designed in Section 6, and the replicas still disagree.
Key Insight: Conflict is not a failure mode of leaderless replication — it is a feature's receipt. We bought 99.99% write availability by letting any coordinator accept any write without asking permission (Section 6's choice over Raft-style leaders). Divergence is the price on that receipt, and this section is where the platform pays it — deterministically, so that all three replicas converge to the same value without a meeting.
The Default: Last-Write-Wins, With the Losses Named
Every write is stamped at the coordinator with a (timestamp, node-id) pair. When two versions of a key meet — during a read, a repair, or a handoff — the higher timestamp wins; on a tie, the higher node-id wins. The tiebreak is load-bearing: it makes resolution a total order, so every replica comparing the same two versions reaches the same verdict independently — convergence without coordination.
Now say the quiet part aloud, because the interviewer is waiting for it: one acknowledged write silently vanishes. The green cart's client got a success response, and its bytes will never be read again. For a shopping cart that can genuinely mean a lost item. We choose LWW as the platform default anyway, with the loss priced: the conflict window is milliseconds wide on a key that one user mutates at human speed, and the alternative — pushing merge logic onto every tenant — has a documented body count we'll get to shortly. Tenants who cannot tolerate a silent loss have a named exit: the conditional put from Section 4, a quorum-serialized compare-and-swap at roughly 3–4× write latency. Price the guarantee; let the tenant choose.
Quick check before moving on:
Sign in to continue reading
The rest of this lesson is available with a free account. Signing in with Google or Microsoft is free.
Sign in to read the full lesson