The question this answers
What does CAP actually constrain, and why is "pick two" the wrong way to say it?
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.
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.
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.
| Term | What people usually mean | What the theorem means |
|---|---|---|
| Consistencyprotocol | "The data is right" / ACID's C / serializable | Linearizability of a single register — real-time-ordered single-object operations |
| Availabilityprotocol | High 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. |
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.
- node-1 — majority side
- node-2 — majority side
- node-3 — a client just asked it for x
- 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.
- • 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.
- • 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.
- • 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.
- • 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.
- • 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.
- • Detect: distinguish partition from node failure using peer-to-peer reachability, not just client-observed errors. Clients see the same symptom for both. See No Heartbeat Does Not Mean Dead.
- • Contain: decide *in advance* which side stays available and for which operations, and make the other side fail fast rather than degrade silently.
- • Recover: on heal, a linearizable system resumes serving the minority side with no data work; an available system begins conflict resolution.
- • Reconcile: for the available choice, this is the expensive part — merging divergent histories is application work no store can do generically. See Two Writes, No Order, One Answer Required and Reconciliation Is a Component, Not a Cleanup Script.
- • Verify: test partition behaviour deliberately rather than discovering it. Partition your own cluster on purpose and observe what each side does. See Chaos Engineering Is Not Randomly Breaking Production and Fault Injection: The Catalogue, and Which Faults Are Hard.
- • 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.
- • 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.
- • 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.
- • PACELC, which adds the else-case CAP omits and is more useful for everyday design. See PACELC: The Trade-off That Exists When Nothing Is Broken.
- • Reason about the specific invariant instead of the model: "can two users claim this handle during a partition?" is more actionable. See Start From the Invariant, Not From the Architecture.
- • Harvest and yield (Fox and Brewer): rather than up-or-down, degrade the *completeness* of an answer — return partial results from the reachable data. Often the best real answer, and invisible to CAP's binary framing. See Graceful Degradation: Which Dependency Is Actually Critical.
- • Design so that no operation needs cross-partition coordination, which makes the dilemma inapplicable. See Coordination Avoidance: Restructuring the Problem Instead of Paying for It.
The partition is here. Answer, or refuse?
- A — answering from local state
- B — answering
- C — answering
- 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.
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 people believe, and what is true
Pick any two of consistency, availability and partition tolerance.
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.
We chose CP, so our reads are consistent.
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.
We chose AP, so we are always up.
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.
Partition tolerance means handling network failures gracefully.
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.
CAP tells us how to design our system.
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.
Partitions are rare, so CAP is mostly theoretical.
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
- 🔧 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.
- ⚡ 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.
- 💬 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?