A clock reading is an interval, not a point — TrueTime's [earliest, latest] of half-width ε. Order two causally-dependent transactions (B reads what A wrote) whose real gap is smaller than ε, and a naive point timestamp puts B before A 38.9% of the time. Spanner's commit-wait — sleep out the uncertainty before revealing the write — drops that to 0, for a fixed cost of 2ε = 8.0 ms per transaction.
A real clock is not a point. Read it and you get an interval — TrueTime's [earliest, latest] of half-width ε — and the true instant is somewhere inside; you cannot know where. Give each transaction a commit timestamp from its interval and try to order two causally-dependent transactions (B reads what A wrote, so B happens-after A) separated by a real gap smaller than ε. Their intervals overlap, so you cannot tell which timestamp is later — and picking a point out of each interval orders them backwards (B before A) 38.9% of the time. Now apply Spanner's fix: after choosing a timestamp, wait until the true time has certainly passed the interval's latest before revealing the write. Any later transaction's earliest is now provably above this one's latest; the intervals never overlap; the ordering errors drop to 0. The surprise: the fix for a too-imprecise clock is to make the system slower on purpose — and the wait equals the uncertainty, 2ε. Tighten the clock and the wait shrinks with it.
Designing Data-Intensive Applications, 2nd ed. (Kleppmann & Riccomini, 2025), Chapter 9 — The Trouble with Distributed Systems, §"Clock readings with a confidence interval" and §"Synchronized clocks for global snapshots":
Rather than thinking of a clock reading as a point in time, it is more helpful to think of it as a range of times, within a confidence interval [...] Spanner deliberately waits for the length of the confidence interval before committing a read-write transaction.
The book presents TrueTime's interval and Spanner's commit-wait as the way to make timestamps safe for external consistency — if A causally precedes B, A's timestamp is less than B's. Here you watch the intervals overlap, watch the naive point-timestamp order causal writes backwards, and watch commit-wait close the gap for a fixed latency cost.
The whole result turns on one move: treating a clock reading as an interval, not a point. These help; each line notes where it shows up.
offset in [-ε, +ε].GAP ms later.[earliest, latest] asserts the true value lies inside, not at any particular point. It shows up as IntervalClock.now() returning (reading - ε, reading + ε).commit_wait=True branch that advances the true clock past latest.[earliest, latest], and two readings are safely orderable only if their ranges don't overlap. TrueTime & commit-wait (#4): Corbett et al., "Spanner: Google's Globally-Distributed Database" (OSDI 2012), §3 — the TT.now() interval, the TT.after() predicate, and the commit-wait rule commit_ts < TT.now().earliest. Clock sync & causality (#1–#2): DDIA §"Unreliable Clocks" and §"Relying on Synchronized Clocks"; Lamport, "Time, Clocks, and the Ordering of Events" (1978) for happens-before. If only one is new, make it #3-into-#4 — the interval clock is the idea the entire exercise rides on.
A point timestamp pretends the clock is exact. An interval timestamp admits it isn't. Commit-wait converts that admitted uncertainty into latency so that order becomes provable.
latest), and use that to order transactions. Because the number is off from true time by up to ε, two transactions whose true times differ by less than 2ε can get numbers in the wrong order. Nothing errors; the order is just silently wrong.latest as the commit timestamp, then do not reveal the write until the true time has certainly passed latest (until now().earliest > commit_ts). Any transaction that starts afterwards reads the clock strictly later, so its earliest sits above this transaction's latest. The intervals are disjoint by construction; order is provable. The wait is the interval width, 2ε.Both paths run the same causal workload — B always reads what A wrote — and assign timestamps the same way. The only difference is whether A sleeps out its uncertainty before B is allowed to see it. The whole thing is one script, code/commit_wait.py.
cd study/ddia/ch09/code
python3 commit_wait.py
Do not read the output yet — make each prediction first.
Predict. A commits at true time 0; B reads A's write 1.0 ms later. The clock's uncertainty is ε = 4.0 ms, so each reading is a ±4 ms interval. B happens after A in real time — will B's interval earliest sit above A's interval latest (clean, orderable), or will the two intervals overlap?
The intervals overlap — B's earliest (−6.800) is far below A's latest (+5.115), even though B really happened 1 ms after A. A 1 ms real gap is invisible inside ±4 ms of clock slop.
Predict. Assign each transaction its interval's latest as the commit timestamp, run 10000 causal pairs (B after A by 1 ms every time), and count how often B's timestamp lands at-or-before A's — a causality violation. Roughly what fraction: near 0%, a few %, or tens of %?
38.9% — more than a third of causally-ordered pairs get timestamped backwards. The clock didn't error; it confidently returned numbers that put the effect before the cause.
Predict. Now A holds its write until the true time has certainly passed its interval's latest, and only then may B read it and commit. What happens to the ordering-error rate — and what does that safety cost per transaction?
Zero errors — every causal pair now orders correctly — bought with a fixed 8.0 ms wait per transaction, exactly 2ε. By waiting out its own uncertainty before revealing the write, A guarantees that anything B reads afterwards is stamped strictly later.
Predict. Shrink ε (a better clock: GPS, atomic). The commit-wait was 2ε. As ε goes 8 → 4 → 2 → 1 → 0.5 ms, what happens to the wait — and to the un-waited error rate?
The wait tracks the clock: it is always 2ε, so halving ε halves the wait. The un-waited error rate falls too, and once ε drops to half the causal gap (ε = 0.5, so 2ε = 1.0 = the gap), the intervals can no longer overlap and even the naive point timestamp is safe.
Two events are safely orderable by timestamp only if the uncertainty intervals you read for them do not overlap. That is the whole game. A clock synchronized to within ε of true time cannot hand you the true instant — the honest thing it can return is the range [reading − ε, reading + ε], which is guaranteed to contain the true time but pins it nowhere. When you collapse that range to a point to get a comparable number, you reintroduce an error of up to ε in each direction. For two transactions whose true times differ by δ, their points can cross whenever δ < 2ε — and here δ = 1 ms while 2ε = 8 ms, so crossing is common, not rare. That is exactly the 38.9%: the timestamps are not wrong about the intervals, they are just being forced into a total order the intervals don't support.
The only way to guarantee two intervals don't overlap, when the second event is caused by the first, is to make sure the first event's interval is entirely in the past before the second is even allowed to happen. That is commit-wait. A picks its commit timestamp s = latest, then refuses to reveal its write until a fresh clock reading's earliest exceeds s — which, since earliest(t) = t + offset − ε and s = 0 + offset + ε, happens exactly when the true time reaches 2ε. So A sleeps for 2ε. Now B, which cannot start until A's write is visible, reads the clock strictly after that instant; its whole interval sits above s; its timestamp is provably greater. The uncertainty didn't go away — it was paid off as latency. And because the wait is precisely the interval width 2ε, the arithmetic gives the punchline directly: buy a better clock, halve ε, and the mandatory wait halves too. Spanner runs GPS and atomic clocks in every datacenter for exactly this reason — not to know the time, but to keep ε (and therefore the wait) small.
GAP above 2 * EPSILON and watch the un-waited error rate fall to 0 on its own — when the real causal gap already exceeds the uncertainty, the intervals can't overlap and commit-wait becomes redundant.Sources: DDIA 2e, Ch. 9, §"Clock readings with a confidence interval", §"Synchronized clocks for global snapshots" · Corbett et al., "Spanner: Google's Globally-Distributed Database" (OSDI 2012) · Lamport, "Time, Clocks, and the Ordering of Events in a Distributed System" (1978).