Consistency Models

CAP: What the Theorem Actually Says

Not "pick any two of consistency, availability and partition tolerance" — that framing is wrong and has misled a generation of design discussions. The theorem says that during a network partition, a system cannot be both linearizable and available for every request at every non-failing node. Partitions are not a choice you make; they happen to you.

▶ Run the lab

The question this answers

The question

What does CAP actually constrain, and why is "pick two" the wrong way to say it?

The guarantee — the property claimed, and its scope

The theorem (Gilbert and Lynch, 2002) is an impossibility result: in an asynchronous network where messages between nodes may be arbitrarily lost or delayed, no implementation of a read/write register can guarantee both linearizability and availability — where availability means every request received by a non-failing node terminates with a response — in every execution. It is a statement about a single register in a specific model, not a taxonomy of databases.

Everything below is bought to hold this sentence. "Strongly consistent" with no scope attached is a slogan, not a guarantee — read what it actually covers, and what it explicitly does not.

What a node knows — observation versus inference

This is the heart of it. A node cut off from its peers cannot distinguish a partition from peers that have crashed, from peers that are merely slow, or from its own link having failed. It receives a request and must choose between two actions with no information to choose on: answer from possibly-stale local state, or refuse to answer. The theorem is really a statement about that node's ignorance — see Crashed or Just Slow: The Distinction You Cannot Make and No Heartbeat Does Not Mean Dead.

A node knows its own state and the messages that arrived. Everything else is inference from evidence that was already stale. "B has not replied in five seconds" is knowledge; "B is down" is a decision — and usually the bug.

What guarantee?What does a node know?How does it work?What can fail?How does it fail?Where is coordination?What holds under failure?How does it recover?How would you know?What is the simpler thing?
CAPpartitionlinearizabilityavailability

The three terms, defined the way the theorem defines them

Almost every misuse of CAP comes from using the everyday meaning of one of the three words rather than the technical one. All three are narrower than they sound.

C is linearizability, and nothing else. Not "the data is consistent", not ACID's C (which is "transactions preserve your invariants" and is unrelated), not serializability. Specifically: single-object operations appearing to happen at an instant, in real-time order. See Linearizability: An Operation Is an Interval, Not a Point.

A is that every request to every non-failing node returns a response. This is much stricter than operational availability. A system with 99.99% uptime is not "available" in CAP's sense if *any* non-failing node ever fails to answer. A system that redirects clients to the majority side is not available, because the minority-side node did not answer. A system with a five-second timeout is not available, because a response that never comes is not a response.

P is tolerating arbitrary message loss between nodes. And here is the crucial part: it is not a property you choose. The network will drop and delay messages. You do not get to build a system in which partitions do not occur; you only get to decide what your system does when one happens. "We chose CA" describes a single-node system, or a system that will be incorrect during its first partition.

TermWhat people usually meanWhat the theorem means
Consistencyprotocol"The data is right" / ACID's C / serializableLinearizability of a single register — real-time-ordered single-object operations
AvailabilityprotocolHigh uptime, an SLO, "the service works"EVERY request to EVERY non-failing node terminates with a response. No timeouts, no redirects, no errors.
Partition toleranceprotocol"We handle network failures gracefully"The guarantees hold in executions where arbitrary messages between nodes are lost. Not optional — it is the failure model, not a feature.
The everyday meaning versus the theorem's meaning

Why "pick two" is the wrong sentence

The "pick two of three" formulation invites you to imagine three symmetric options, one of which you decline. That is not the shape of the result. Partitions are imposed by the world. Any system spanning more than one machine will, at some point, have one node unable to reach another — through a cable, a switch, a misconfigured firewall rule, a saturated link, a long garbage-collection pause that makes a node indistinguishable from an unreachable one.

So the real statement is conditional and narrow: when a partition occurs, and a request arrives at a node on the wrong side of it, that node must either answer (giving up linearizability, because the answer may be stale) or not answer (giving up availability). There is no third option, and no clever engineering removes the dilemma, because the node genuinely does not know what the other side has done.

And note what the theorem does *not* constrain: behaviour when there is no partition. CAP is silent about the 99.9% of the time the network works, which is where all your latency lives. That silence is the gap PACELC: The Trade-off That Exists When Nothing Is Broken fills.

A request arrives on the minority side. Two choices, no third.protocol
n1 ↔ n2: okn1 ↔ n3: partitioned — no traffic crossesn2 ↔ n3: partitioned — no traffic crossesnode-1 · leader · up — majority sidenode-1★ leadernode-2 · follower · up — majority sidenode-2· followernode-3 · follower · isolated — a client just asked it for x⦸ node-3· followerisolatedpartitionedpartitioned
okpartitioned
  • node-1 — majority side
  • node-2 — majority side
  • node-3 — a client just asked it for x
What each node believes
  • n3believes “my value of x may be current”✕ and it is false
  • n3believes “the other nodes may have crashed”✕ and it is false
  • n1believes “node-3 is down”✕ and it is false

Every node above is acting on what it believes. Nothing in the cluster tells the mistaken one that it is mistaken.

Most real systems are neither CP nor AP

Because the definitions are strict, the labels almost never fit. Most "CP" systems are not linearizable. A database with consensus-replicated writes and snapshot-isolated or replica-served reads does not provide linearizability on its read path, so it fails CAP's C in normal operation — before any partition. Most "AP" systems are not available in CAP's sense. A leaderless store configured with majority quorums returns errors on the minority side; a system with client timeouts fails to respond; a system that redirects is not answering at the node that received the request.

This is not pedantry — it changes what you should do with the theorem. CAP is a proof that a particular pair of properties is unattainable, not a classification scheme for products. Using it as a taxonomy produces confident, wrong statements ("we picked AP so we are always up") that survive because nobody checks them against the definitions.

The genuinely useful residue is small and important: *if* you need linearizability, you must accept unavailability somewhere during a partition, and you should decide in advance which side goes down and how it behaves. *If* you need every node to answer always, you must accept that answers can be stale and design a merge. That decision — made deliberately, per operation, with the failure behaviour specified — is what CAP is for.

  • A system that times out is not available in CAP's sense; a response that never arrives is not a response.
  • A system that redirects to the majority is not available at the node that received the request.
  • A system serving reads from replicas is not linearizable, so it fails C even with no partition present.
  • The useful question is never "are we CP or AP" but "for this operation, during a partition, do we answer or refuse?"

Key points

  • The theorem: during a partition, no system provides both linearizability and a response from every non-failing node.
  • C is linearizability specifically. A is every request at every non-failing node getting a response. P is the failure model, not a feature.
  • Partition tolerance is not optional — partitions happen to you, so "CA" describes a single node or a system that will be wrong.
  • The real choice is conditional: during a partition, does this node answer with possibly-stale data or refuse to answer?
  • Most production systems are neither CP nor AP under the strict definitions, so the labels mislead more than they explain.
  • CAP says nothing about behaviour when the network is healthy — that is what PACELC addresses.

The chain, answered

Every field here is required, which is why no lesson in this domain can recommend a design without naming what an operator sees when it fails, what survives the partition, what repairs it afterwards, and the simpler thing to consider first.

How it works
  • Assume an asynchronous network in which messages between nodes may be lost or delayed without bound.
  • Construct a partition splitting the nodes into two groups that cannot communicate.
  • A write is accepted on one side; a read arrives on the other.
  • If the reading node responds, it must respond from local state, which cannot reflect the write — so the history is not linearizable.
  • If it does not respond, availability is violated by definition.
  • Since the reading node cannot distinguish this execution from one where no write occurred, no algorithm can choose correctly. The impossibility follows.
What can fail at the boundary
  • A link fails and two halves of the cluster each continue independently.
  • A node is not partitioned but is paused long enough (GC, hypervisor stall, disk stall) to be indistinguishable from partitioned.
  • An asymmetric partition: A can reach B but B cannot reach A, so each forms a different view of the cluster.
  • A partition that isolates the majority from clients rather than from each other, so the "healthy" side serves nobody.
  • A partition heals and both sides must reconcile divergent state.
How it fails — what an operator sees
  • Minority-side unavailability read as an outage: after a partition, requests to isolated nodes return errors or hang. This is a linearizable system behaving correctly, and it will be reported as "the database went down". Observable as a clean error-rate step confined to a subset of clients.
  • Divergence after an available-side choice: both sides accepted writes, and after heal the operator finds two versions of records with no automatic resolution. Observable as duplicate keys, conflicting balances, or records with two update histories.
  • Split-brain from a stale leader that kept answering: the old leader was partitioned but continued serving reads and writes, so acknowledged writes exist that the new leader never saw. Observable as data that vanishes after the partition heals. See Split-Brain: Two Nodes, Both Certain They Are In Charge and Fencing Tokens: Making the Stale Actor Safe, Not Just Unlikely.
  • Believed-CP system serving stale reads: a team assumed linearizability because the system is "CP", but reads were served from followers. Observable as stale reads in steady state, unrelated to any partition, and deeply confusing to whoever is holding the CAP diagram.
  • GC-pause pseudo-partition: a node stalls for 20 seconds, is declared dead, a new leader is elected, and the stalled node wakes up and acts on its old belief. Observable as writes from a node everyone had already replaced.
Where coordination is required
  • Linearizability requires an operation to confirm no newer state exists elsewhere, which requires reaching other nodes — that requirement is precisely what a partition denies.
  • Availability in CAP's sense requires operations to complete with local information only, which forbids that confirmation.
  • The two requirements are contradictory during a partition. This is not an engineering gap; it is a proof. See Coordination Couples Availability.
What still holds under failure
  • A linearizable system keeps its guarantee and loses availability on the side that cannot reach a quorum.
  • An available system keeps answering everywhere and loses linearizability, diverging until the partition heals.
  • Real systems are usually mixed: linearizable for some operations, available for others, and the mapping is a design decision that should be written down.
How it recovers
How you would know
  • Peer-to-peer reachability matrix, so a partition is distinguishable from node failures.
  • Which side of a partition each request was served by, and whether it was answered or refused.
  • Quorum-loss error rate, separated from general errors — this is the visible price of choosing linearizability.
  • Post-heal conflict count and unresolved-conflict backlog, which is the visible price of choosing availability.
  • Long-pause detection (GC, hypervisor stalls) at every node, since a pause is a partition from the cluster's perspective.
When it helps
  • Framing a design decision about behaviour during partitions, per operation, before the partition happens.
  • Cutting through a vendor claim that a system offers linearizability and full availability simultaneously — the theorem says it does not.
  • Explaining to stakeholders why the correct behaviour of a consistent system during a partition looks like an outage.
When it hurts
  • As a taxonomy of databases, where it produces confident wrong statements and stops useful analysis.
  • For reasoning about normal operation, which it says nothing about — use PACELC or plain latency analysis instead.
  • For multi-object or transactional guarantees, which the theorem does not address at all; it is about a single register.
  • As a reason not to think: "we are AP" is frequently used to avoid specifying what actually happens during a partition.
Simpler alternatives

The partition is here. Answer, or refuse?

The partition is here. Answer, or refuse?
A is cut off from B and C. Requests keep arriving at both sides. A node that cannot reach its peers must choose between answering from possibly-stale local state and refusing to answer — and it must choose with no information about which is happening.
what a node that cannot reach its peers does
requests answered
4 of 4
requests refused
0
linearizable
not-linearizable
conflicts to reconcile
1
A is isolated; B and C can still reach each otherassumption
A ↔ B: partitioned — no traffic crossesA ↔ C: partitioned — no traffic crossesB ↔ C: okA · follower · isolated — answering from local state⦸ A· followerisolatedB · leader · up — answeringB★ leaderC · follower · up — answeringC· followerpartitionedpartitioned
partitionedok
  • A — answering from local state
  • B — answering
  • C — answering
What each node believes
  • Abelieves “B and C have crashed”✕ and it is false
  • Bbelieves “A has crashed”✕ and it is false

Every node above is acting on what it believes. Nothing in the cluster tells the mistaken one that it is mistaken.

Every request that received an answerprotocol
c1 → A write(1) → ack; invoked at 0, responded at 2; no effect point placedc1 → Awrite(1)no effect point placed→ ackc2 → B write(2) → ack; invoked at 1, responded at 3; no effect point placedc2 → Bwrite(2)no effect point placed→ ackc3 → A read → 1; invoked at 4, responded at 5; no effect point placedc3 → Areadno effect point placed→ 1c4 → B read → 2; invoked at 6, responded at 7; no effect point placedc4 → Breadno effect point placed→ 2t=0real time →t=7
invocation → response: the op is this whole intervaleffect point — one instant inside the interval
✕ Not linearizable

Search exhausted after 6 states: no placement of effect points inside the operations' intervals produces a legal sequential history that also respects real-time order. The search could never place c3 → A's read: it observed 1 at a point where the register necessarily held 2.

What availability cost
Every request received a response from the node that received it. That is availability in the sense the theorem uses — not uptime, not a status page: every request at every non-failing node terminates with an answer.
What consistency cost
The history is not linearizable, and the checker searched exhaustively rather than guessing. Two clients wrote, both were acknowledged, and two later reads returned different values — there is no single-copy story that explains this execution.
…and the work does not end when the partition does
The two sides now hold different values, and healing does not merge them by magic. With last-version-wins the cluster converges on x=2 at step 9, and the other acknowledged write is discarded. Choosing to stay available is choosing to do this reconciliation — deliberately, with a rule you picked, or accidentally, with the rule your library ships.
The choice is forced, and neither answer is the right one in general. Refusing preserves linearizability and gives up availability on the isolated side; answering preserves availability and gives up linearizability, creating a conflict someone must reconcile. What the theorem rules out is having both, for a register, in an asynchronous network — nothing else. "Pick two" is the wrong sentence because P is not something you pick: partitions happen to you, so a system described as "CA" is either a single node or a system that will be wrong. Notice also what the whole result reduces to: a node cut off from its peers cannot distinguish a partition from a crash, from slowness, from its own link failing. It must act, and it has nothing to act on. And when the network is healthy — which is nearly always — CAP says nothing at all; that is the question PACELC picks up.
assumptionThe impossibility result is about a read/write register in an asynchronous network where messages may be lost or delayed arbitrarily. The verdict on each history is computed; the mapping from “this configuration” to “this is a CP/AP database” is not, and is usually wrong.

What people believe, and what is true

Claim

Pick any two of consistency, availability and partition tolerance.

Reality

This is the wrong framing and the theorem does not say it. Partition tolerance is not selectable — partitions are imposed by the network. The theorem constrains only what happens *during* a partition: linearizability and answering-every-request cannot both hold. "CA" is not a design; it is a single-node system, or a distributed system that will be incorrect during its first partition.

Claim

We chose CP, so our reads are consistent.

Reality

CAP's C is linearizability. If any read is served from a replica, or at snapshot isolation, or from a leader that has not confirmed leadership, the system is not linearizable — with or without a partition. The label describes an aspiration, the read path describes the guarantee.

Claim

We chose AP, so we are always up.

Reality

CAP's A requires *every* non-failing node to answer *every* request. A quorum-configured store returns errors on the minority side; a client timeout is not a response. Almost nothing is AP in the strict sense.

Claim

Partition tolerance means handling network failures gracefully.

Reality

It means the guarantees continue to hold in executions where arbitrary messages between nodes are lost. It is a description of the failure model the system is claimed to work in, not a feature you implement well or badly.

Claim

CAP tells us how to design our system.

Reality

It rules out one specific combination during one specific failure. It says nothing about normal operation, nothing about latency, nothing about transactions, and nothing about multi-object guarantees. Most design decisions are outside its scope.

Claim

Partitions are rare, so CAP is mostly theoretical.

Reality

Partitions include any period where nodes cannot exchange messages: a saturated link, a firewall change, a 20-second GC pause, a hypervisor stall. On a cluster of any size these are weekly, not yearly, and the theorem applies to every one of them.

Go deeper

Only the levels this lesson can honestly fill — a missing level is a claim nobody had.

Overview

During a network partition, a node cut off from its peers must either answer with possibly-stale data or refuse to answer. It cannot do both, and it cannot know which is right.

Practical

Stop classifying your system and start specifying behaviour. For each operation, write down what happens during a partition: does the minority side serve, refuse, or degrade? Then test it by partitioning your own cluster on purpose. That document is worth more than any CP/AP label.

Advanced

The formal result (Gilbert and Lynch 2002) proves that in an asynchronous network with arbitrary message loss, no read/write register implementation is both linearizable and available. The proof is a two-execution indistinguishability argument: construct an execution where the write happened and one where it did not, make them indistinguishable to the reading node, and observe that any response is wrong in one of them. The same paper shows that under *partial* synchrony you can recover a weaker guarantee — bounded staleness — which is why real systems are more useful than the strict impossibility suggests. See Consensus Is Not Magic: The Assumptions It Runs On for the same partial-synchrony assumption doing the same work in consensus.

Apply it

Build it, then break it
  • 🔧 For one operation in a system you work on, write down exactly what each side of a partition does, then verify it by injecting a partition rather than by reading the documentation.
Reason about this
  • A two-region deployment loses the inter-region link for 40 minutes. Specify, per operation class, which side serves and what reconciliation is required afterwards.
Interview questions
  • 💬 State CAP precisely. Then explain why "pick two" is not a correct rendering of it.
  • 💬 Is your system CP or AP? (The correct answer starts by rejecting the question.)
  • 💬 A node is paused by a 20-second GC. Is the cluster partitioned? Argue both ways and then say which matters.
  • 💬 What does CAP tell you about your system's behaviour when the network is healthy?