External consistency, and what is verifiable
How is external consistency's real-time anchor bought?
External consistency, and why it exists
Strict serializability is serializability plus real time: if T1 commits before T2 begins in wall-clock terms, every observer sees T1 first. Equivalently, it is linearizability generalized from operations to transactions. Spanner calls it external consistency.
It needs its own name because neither parent is enough. Serializability has no recency: Transactional isolation showed that a system returning the initial state forever is serializable. Linearizability has no multi-object atomicity: Distributed consistency showed that it is defined per object. Their conjunction is strictly stronger than either, and it is the only cell in the combined table with no anomalies left.
It costs the union of both bills. The real-time anchor is bought in exactly one of three ways:
- Bounded clock uncertainty, paid as commit wait (Spanner).
- A communication round to an authority (a timestamp oracle; Percolator).
- Optimism, paid as restarts (CockroachDB's uncertainty intervals).
The uncertainty ε is conserved. Only the invoice changes. How commit wait and uncertainty restarts work is Bounded clock uncertainty.
The justification most people never hear: almost nobody needs external consistency for reasons internal to the database. They need it because of a second channel (a human, a message queue, a separate service) that carries information faster than replication does. That is exactly the hidden-causality problem of Distributed consistency, and external consistency is the only model that closes it, because real time is the one reference frame that every out-of-band channel also inhabits. Buying external consistency is buying immunity to the causality you cannot observe.
So "do we need it?" becomes answerable: do information paths exist around your system that are faster than your replication lag? For anything user-facing across two regions, yes. (Example: Anika sends money, her app shows "sent", she phones Sam, and Sam opens his app in another region.)
What is actually verifiable
Closing this mechanism requires admitting how little of it can be checked.
Linearizability checking is NP-complete for general histories, and search-based checkers blow up on realistic traces. The practical approach inverts the problem. Record operations designed so that the version order can be recovered afterwards (list-append, monotone counters). Infer the dependency graph. Look for cycles. Then classify the cycles using exactly the shapes from Transactional isolation.
Example of a list-append test. Each transaction appends a unique number to a list stored in a row, and reads other lists. After the run, each list's final order reveals the order in which writes were applied, so the checker knows every write-write and write-read dependency without trusting the database. It then looks for a cycle and names it (for example a cycle with two anti-dependency edges is write skew).
What cannot be checked at all: any liveness claim, and any property requiring knowledge of operations that did not occur.
The operational consequence, stated bluntly: a vendor's isolation level is a hypothesis, and a cheap one to test. Independent testing has repeatedly found claimed levels unmet. The pattern is not incompetence. The levels were specified against one family of mechanisms and implemented with another, which is the same root cause as the naming mess in Transactional isolation. How to build a testing method is measurement's subject; this mechanism's contribution is knowing what to look for.