Data Consistency
Pick a consistency model from what each piece of data can tolerate, price it with PACELC, and defend the choice per item instead of per system.
Once data lives on more than one machine, "what is the current value?" stops having a single answer. Consistency models are the vocabulary for stating how stale a read is allowed to be — and every step toward fresher reads is paid for in latency or availability.
What the word means here
Consistency means every replica presents the same view of a data item — or a precisely stated approximation of it. The definition is easy; the physics is not: messages take time, operations arrive concurrently, and a partition can drop them entirely. A read landing mid-propagation must return something, and the model is the contract that says what.
One vocabulary collision first: the C in ACID and the C in CAP are unrelated. ACID-C means a committed transaction leaves the database's invariants intact — no negative balance, no orphaned foreign key. CAP-C means every read observes the most recent acknowledged write. Single-node Postgres is fully ACID and silent on CAP-C: no replicas to disagree.
Strong consistency
The strictest model: any read returns the most recent successful write, because replicas are synchronised before that write is acknowledged. The usual mechanism is a quorum. With N replicas, a write acknowledged by W and a read answered from R overlap on at least one replica when R + W > N. N=3, W=2, R=2 overlaps; N=3, W=1, R=1 does not — which is what a fast, eventually consistent configuration buys.
You get logic that never reasons about stale data, where correctness is load-bearing: money, inventory, seat reservation. You pay latency on every write, and errors when replicas cannot reach each other. You meet it in ACID databases, synchronous replication, and consensus algorithms like Paxos or Raft.
That latency is geography, not algorithm. Three replicas in one datacenter cost one extra round trip of roughly 0.5 ms — invisible next to any real query. Stretched from Mumbai to Virginia the same quorum costs roughly 200 ms per write: a 400x increase for the identical protocol. Every write pays it, so it lifts the mean and the p99 by the same ~200 ms — a floor, not a tail problem. Tail work chases a slow minority; nothing there removes a cost every request incurs. The only lever is where the replicas live.
Eventual consistency
The weaker model. If no new updates arrive for an item, eventually every read of it returns the last written value; until replicas converge, a read may be stale. Reads and writes never wait for the slowest replica, the system keeps serving through partitions and replica failures, and it scales horizontally cheaply — BASE, asynchronous replication, and NoSQL stores such as Cassandra and DynamoDB.
Name the failure lag produces rather than filing it as a bare "con". A user edits their display name; their next read is balanced onto a follower 200 ms behind, and the page renders the old name. They conclude the save failed and do it again. Read-your-writes has broken, and the symptom is a user watching their own update disappear — the real cost of replication lag.
The models in between
Causal, session, monotonic-read and read-your-writes consistency sit between the extremes, because the expensive guarantee is usually needed by exactly one client: the one that just wrote. Two mechanisms deliver it without a quorum on every read.
- Pin the session. For a bounded window after a write — a few seconds, longer than replication lag — route that user's reads to the leader; everyone else keeps reading followers.
- Carry a version. The write returns a log position (LSN, timestamp, version vector); the client sends it back on the next read, and a follower behind it waits or forwards to the leader.
Pricing the pin takes two numbers, not one. At 1,000 QPS with a 10:1 read/write ratio, writes are ~90/s and reads ~910/s. State comes from Little's Law: 90 writes/s × a 5-second window = 450 sessions pinned at once — an expiring map, not a capacity problem. Load is separate, and needs a stated assumption: how many reads does a writer issue in that window? Assume one, the read-back that would otherwise be stale, and the leader takes ~90 extra reads/s beside its 90 writes/s, on a box good for ~5,000 simple QPS. Routing every read there instead sends all ~910 reads/s — ten times the pinned-read load.
CAP, stated precisely
- Consistency (C): every read sees the most recent write.
- Availability (A): every request gets a non-error response, with no guarantee it is current.
- Partition tolerance (P): the system keeps operating when messages between nodes are dropped or delayed.
Partitions are not opt-in — cables are cut, switches fail, a rack loses its uplink. The only decision is what the system does while one is happening. CP stops serving rather than return data that may be wrong: a payment ledger. AP keeps serving, possibly stale, and reconciles on heal: a feed or a like count. There is no third option — "CA" has assumed partitions away.
PACELC: the part CAP leaves out
CAP describes a failure mode that, on a healthy network, almost never occurs. PACELC covers the rest of the time:
If there is a Partition, choose Availability or Consistency — Else, choose Latency or Consistency.
The else-branch is the one you live with daily: that quorum write costs ~200 ms across regions, while a local follower answers in under a millisecond and may be stale by the replication lag.
| System | Partition | Else (normal operation) |
|---|---|---|
| DynamoDB (default) | PA — stays available | EL — favours latency |
| Spanner | PC — refuses rather than diverge | EC — favours consistency |
| Cassandra (tunable) | PA | EL, until you raise the consistency level |
In an interview
What is tested is whether you attach a consistency requirement to each piece of data and can quote its price. A design holding both an account balance and a like count asks whether you treat them the same way.
- Split the data first. "The balance needs a serialised write path and read-your-writes. A profile's follower count can lag thirty seconds and nobody notices."
- Quote the price with the arithmetic attached. "A two-region quorum write costs ~200 ms, every time. Reservations run in the tens per second — assume 50/s — so Little's Law gives 50/s × 0.2 s = 10 writes in flight, which is nothing. The same 200 ms across ~910 read QPS is ~180 reads in flight and a 200 ms floor under every page, so reads go local."
- Name the partition behaviour. "PA/EL: during a partition it stays up and diverges; otherwise it answers locally rather than waiting for a quorum."
- Justify store and replication mode together — SQL or NoSQL, synchronous or asynchronous — since those choices implement the guarantee you claimed.
Declaring one consistency level for the whole system loses the most points: "eventual consistency for scale" says you have not looked at the individual items, and puts the money path on a store that can lose a write. The runner-up is reciting CAP as "pick two of three".
Check yourself
1. Read/write ratio is 10:1 at 1,000 QPS. Users report their own profile edits "not saving". Followers lag ~200 ms at p99. Move reads to the leader?
No — that buys a guarantee for everyone to fix a problem only the writer has. Writes are ~90/s, reads ~910/s. Pinning each writer for 5 s holds 90 × 5 = 450 sessions at once: routing state, not load. The load is the reads those sessions issue — at one read-back per write, ~90 extra reads/s on a leader good for ~5,000 simple QPS. All ~910 reads/s is ten times that.
2. A like counter and airline seat reservation, one product. Same database, same consistency setting?
Same database is fine if it offers per-operation consistency levels; the same setting is not. Likes are commutative and a two-second lag is undetectable, so
W=1and eventual convergence is cheap and correct at volume. A seat is a contested single item where two last-write-wins updates double-book the flight, so it needs a serialised decision at quorum. Pay the ~200 ms on the ~50 reservations/s priced above; refuse it on millions of likes.