Why Spanner's Writes Cost a Few Extra Milliseconds — and Why That's the Whole Point
Spanner guarantees that any read after a commit sees that write — globally. The trick is TrueTime's bounded clock uncertainty and a deliberate commit wait of a few milliseconds per write.
21 Feb 2026, 20:08 UTC

The problem: "did my write actually happen?"
If you've ever built on a distributed database with eventual consistency, you know the drill: a user updates their profile, hits refresh, and sees the old value. Your application now needs retry logic, read-your-writes tokens, or conflict-resolution code — all because the database can't promise that a read after a write will see that write.
Google Cloud Spanner takes a different bet. It guarantees external consistency (often described as linearizability across the whole database): if transaction A commits before transaction B starts, B sees A's effects. Globally. Across regions. The interesting engineering question is how — and the answer is a deliberate, measurable delay on every write.
TrueTime: a clock that admits it's uncertain
Most systems pretend timestamps are exact. Spanner's TrueTime API does the opposite: it returns an interval, [earliest, latest], within which the true current time is guaranteed to fall. The width of that interval is called ε (epsilon), and it's derived from time sources that combine GPS receivers and atomic clocks in Google's data centers, cross-checked by time masters.
Because every node agrees on a bounded uncertainty, Spanner can assign each transaction a commit timestamp that is globally meaningful — not just meaningful on one server. That timestamp becomes the ordering primitive for the entire database.
Commit wait: paying ε to buy ordering
Here's the mechanism that surprises people the first time. When a read-write transaction commits, Spanner picks a commit timestamp s and then waits until TrueTime's earliest possible time is past s. In other words, it waits out the uncertainty interval before acknowledging the commit to the client.
Why? If the client receives the commit acknowledgment, real time has definitely passed s. Any transaction that starts afterward will get a timestamp greater than s and therefore must see the write. The guarantee falls out of the physics of the clock rather than from coordination between nodes at read time.
The cost is direct: commit wait latency is proportional to ε. In production Spanner, ε is typically a few milliseconds, so writes carry a predictable, bounded overhead. That's the trade — a few milliseconds per commit in exchange for never writing conflict-resolution logic.
Seeing it in practice
You can observe both sides of this trade-off on a real Spanner instance (the open-source emulator won't show it — it lacks the GPS/atomic-clock hardware and TrueTime semantics):
- Check the guarantee. Run a read-only transaction with exact staleness of zero (a strong read) immediately after a commit returns. The read reflects every transaction that committed before it started — no tokens, no retries. In the client libraries, this is the default strong read; for example, a single-use read-only transaction in the Python or Java client performs a strong read at the current timestamp.
- Measure the cost. Open Cloud Monitoring and look at Spanner write latency metrics, breaking down commit latency where available. The commit-wait component tracks ε, so you'll see a small, stable floor on write latency that doesn't shrink with a faster network. Compare it against your application's total write latency to decide whether the overhead matters for your workload.
Permissions needed: a Spanner instance you own (or roles/spanner.databaseReader/databaseUser for reads and writes) and roles/monitoring.viewer to inspect metrics. Don't run latency experiments against a production instance serving live traffic — use a test instance with a representative schema.
The trade-off, stated plainly
Commit wait means Spanner writes will never be the lowest-latency option on the market, and ε can grow if clock infrastructure degrades (Spanner is designed to keep it bounded, but the guarantee depends on that hardware). If your workload is write-heavy, latency-sensitive, and tolerant of stale reads — say, telemetry ingestion — an eventually consistent store may fit better, at the price of application-level conflict handling.
But if you're building ledgers, inventory, reservations, or anything where "the read after the write must be correct" is a business rule rather than a nicety, Spanner's few milliseconds are buying you the removal of an entire category of bugs. That's usually a good deal.
What to do next
Spin up a small Spanner instance, run a commit followed immediately by a strong read, and confirm the read-your-writes behavior holds without any application-side machinery. Then check the commit latency metrics and decide whether the overhead fits your SLOs. Because ε values and the commit-wait algorithm can evolve, verify against the current Spanner documentation and release notes for the version you're deploying before you bake latency assumptions into capacity plans.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.