Consistency & CAP
The consistency spectrum from strong to eventual, linearizability, the CAP theorem (and its common misreading), and PACELC's latency tradeoff.
Consistency & CAP
In one line: Once data is copied across machines, "what's the current value?" stops having one obvious answer — you choose a consistency model (from strong, where everyone always sees the latest write, to eventual, where replicas converge over time), and the CAP theorem says that during a network partition you must sacrifice either consistency or availability; PACELC adds that even when there's no partition, you trade consistency against latency.
If your data lives on one machine, "the current value" is obvious — it's whatever's in memory. Once you keep copies on several machines (for durability, for read scaling, for being close to users worldwide), they can briefly disagree, and you have to decide how much disagreement you'll tolerate. Strong consistency means every read sees the most recent write, always, as if there were one copy — simple to reason about, but slower and fragile under network trouble. Eventual consistency means the copies will agree eventually if you stop writing, but in the meantime a read might return stale data — faster and more available, but you must design for "this might be a little out of date." The famous CAP theorem captures the unavoidable catch: when the network splits and machines can't reach each other, you can keep answering requests (stay available) or keep all copies in agreement (stay consistent), but not both. Which you pick depends entirely on what your data is — a bank balance and a like-count want opposite answers.
The consistency spectrum
"Consistent" isn't binary; it's a spectrum of guarantees, each weaker (and cheaper/faster/more available) than the last:
| Model | Guarantee | Feels like | Cost |
|---|---|---|---|
| Linearizable / strong | Every read sees the latest committed write, instantly, as if one copy exists | A single machine | Highest latency; unavailable under partition |
| Sequential / causal | Operations respect causal order (effects never seen before causes), but not necessarily real-time | "Replies never appear before the message they reply to" | Medium |
| Read-your-writes | A user always sees their own prior writes (others' may lag) | "My edit shows up for me immediately" | Low-ish, very common |
| Eventual | Replicas converge if writes stop; meanwhile may disagree | "Refresh and it'll be right soon" | Lowest; most available |
The art is matching the model to the data. A bank balance needs strong/linearizable consistency — showing a stale balance is unacceptable. A social media like-count is fine eventually consistent — "1,204 vs 1,207 likes for a few seconds" harms no one, and the availability/speed win is huge. Most real systems mix models per data type within the same app.
Linearizability, concretely
Linearizability (the strongest single-object model) means: the system behaves as if there's exactly one copy of the data and every operation happens atomically at some instant between its start and finish. Once a write completes, every subsequent read — on any node — sees it (or something newer). No reader ever sees the value go "backwards." It's the intuitive "it just works like one machine" guarantee, and it's expensive precisely because providing it across machines requires coordination (often consensus) on every operation.
The CAP theorem — and its constant misreading
CAP states: a distributed data store can guarantee at most two of:
- Consistency — every read sees the latest write (here, specifically linearizability).
- Availability — every request gets a (non-error) response.
- Partition tolerance — the system keeps working despite the network dropping messages between nodes.
The near-universal misreading is "pick any 2 of 3, like a menu." That's wrong in practice, because partitions are not optional — networks will fail, so P is mandatory for any real distributed system. Which means the actual choice CAP forces is binary and only applies during a partition:
Network partition happens (nodes can't reach each other)
│
┌─────────────────┴──────────────────┐
CP: stay CONSISTENT AP: stay AVAILABLE
refuse/error on the side that keep answering on both sides
can't confirm it has the latest with possibly-stale data;
data → some requests fail reconcile when the partition heals
e.g. a bank ledger, a lock service e.g. a shopping cart, likes, a CDN
So the real question is: when the network splits, would you rather return an error (CP) or possibly-stale data (AP)? For money and locks, error (CP). For carts, feeds, and counters, stale-but-available (AP). When there's no partition (the normal case), you get both C and A — CAP only bites during the failure.
CAP only describes behavior during a partition, which is rare. PACELC completes the picture: if Partition, choose Availability or Consistency; Else (normal operation), choose Latency or Consistency. The "Else" half is the one you pay constantly: even with a perfectly healthy network, providing strong consistency requires nodes to coordinate before answering — which adds latency to every request. Want every read to see the latest write across regions? You pay a round-trip to coordinate. Willing to accept slightly stale reads? You answer instantly from the nearest replica. So consistency isn't only a failure-time concern — it's a latency tax on every single operation, all the time. This is why eventually-consistent systems are fast: they've opted out of that tax. PACELC is the more useful lens for everyday design decisions than CAP itself.
A single e-commerce app deliberately uses three models:
- Account balance / inventory decrement at checkout → strong/CP. Overselling the last item or showing a wrong balance is unacceptable; during a partition, refuse the operation rather than risk a double-sell. Worth the latency and the occasional "try again."
- Product reviews & ratings → eventual/AP. A new review taking a few seconds to appear everywhere is invisible to users; prioritize availability and speed, reconcile asynchronously.
- "Items in your cart" → read-your-writes. You must always see what you just added (or you'll rage), but another device syncing a second later is fine.
Notice nobody picks "strong consistency for everything" — it would make the whole app needlessly slow and fragile — nor "eventual for everything" — it would corrupt money and inventory. Consistency is a per-data-type decision, and knowing the spectrum is what lets you make it deliberately instead of accidentally.
Common mistakes
- Reading CAP as 'pick any 2 of 3.' Partition tolerance isn't optional in a real distributed system; the actual choice is C-vs-A during a partition. Frame it as "error or stale data when the network splits?"
- Choosing one consistency model for the whole system. Strong-everywhere is needlessly slow and fragile; eventual-everywhere corrupts money/inventory. Choose per data type.
- Assuming strong consistency is 'correct' and eventual is 'buggy.' Eventual consistency is a deliberate, valid tradeoff for data that tolerates brief staleness — and it's why such systems are fast and available.
- Ignoring the everyday latency tax (PACELC's 'else'). Strong consistency costs coordination latency on every request, not just during failures. If reads are slow cross-region, over-strong consistency is a prime suspect.
- Surprising users with eventual consistency on their own actions. "I posted it but it's not there!" Use read-your-writes for a user's own data even if the rest is eventual.
Page checkpoint
Did consistency & CAP stick?
RequiredWhat's next
→ Continue to Replication — the mechanics of keeping those copies, and the knobs that set where you land on the consistency spectrum.