The CAP theorem and its trade-offs
Open in the interactive app: practice, quiz, mock interview →In one minute
- CAP says that a replicated data store cannot guarantee both linearizable consistency and availability for every request while the network is partitioned. During a partition, each operation must either wait or fail (keep C) or answer from possibly stale data (keep A).
- It is not "pick two of three". Partitions are not optional in a real network, so the actual choice is what to give up when one happens.
- The C in CAP is linearizability, and the A is a strict "every non-failed node answers". Most real systems offer neither in pure form, which is why labelling a whole database "CP" or "AP" is usually misleading.
- PACELC adds the other half of the story: even with no partition, you trade latency against consistency, and that trade is the one you pay every day.
- Choose per operation, not per system: a seat booking needs C, a product view count can live with A.
How it works
The statement, precisely
Eric Brewer proposed CAP as a conjecture in 2000, and Seth Gilbert and Nancy Lynch proved it in 2002 (Gilbert and Lynch). Their model is a read-write register replicated across nodes. Consistency means linearizability: every operation appears to take effect at a single instant between its call and its return, so a read after a completed write sees that write or a later one. Availability means every request received by a non-failing node eventually gets a non-error response. Partition tolerance means the system keeps its guarantees even when the network drops any number of messages between nodes.
The proof is short. Split the nodes into two groups that cannot talk. A client writes on side 1; another then reads on side 2. If side 2 must answer (availability), it can only return what it has, which is the old value, breaking linearizability. If it must be linearizable, it cannot answer until the partition heals, breaking availability.
Why "pick two" is wrong
"CA" would mean consistent and available as long as there is no partition, which describes every system on a good day. Any network of more than one machine can partition (a switch reboots, a firewall rule changes, a process pauses in garbage collection), so a distributed system cannot opt out. The live question is how it behaves during the partition and how it reconciles afterwards.
Brewer himself wrote in 2012 that the "2 of 3" framing was always misleading, and that CAP's C is a strict subset of the C in ACID: it is about one register's recency, not about preserving every database rule (Brewer, InfoQ). He recommends treating a partition as an explicit mode with three steps: detect that it started, enter a partition mode that limits some operations, and run recovery when communication returns. Recovery is where the work hides, because both sides' writes must be merged and any broken invariants repaired.
"Effectively CA"
Google describes Spanner as technically CP: during some partitions it chooses consistency and gives up availability. Brewer argues it can still claim to be "effectively CA" because its measured availability is high enough, five nines or better, that users can ignore outages, and because partitions cause few of them. In Spanner's internal incident data, the network category was under 8%, and no Spanner quorum was ever on the minority side of a partition (Brewer, 2017). The lesson for everyone else: CAP's availability is an all-or-nothing property, while the availability users feel is a percentage you can engineer with redundant networks.
What the two choices look like
A consistent-under-partition system, such as etcd, ZooKeeper, or a Raft-replicated database, needs a majority quorum for every write and linearizable read. Minority-side nodes refuse or redirect requests; the majority side keeps working.
An available-under-partition system, such as Cassandra at low consistency levels, lets each side keep accepting reads and writes. Both sides may update the same key, so healing needs a merge rule: last-writer-wins, vector clocks with application merge, or CRDTs.
Per operation, not per database
Most databases let you choose per request: Cassandra's consistency levels (ONE, QUORUM, ALL), MongoDB's read and write concerns, DynamoDB's strongly consistent reads. The choice can also be per table. DynamoDB global tables default to multi-Region eventual consistency, where concurrent writes in two Regions are settled by last writer wins; since 2025 a table can instead be created with multi-Region strong consistency, which must span exactly three Regions (or two plus a witness), replicates each write synchronously to at least one other Region, and rejects a write to an item already being changed elsewhere with ReplicatedWriteConflictException (DynamoDB docs). The contention shape shows where C is required: the last seat, a bank balance, a unique username. Where staleness is harmless or repairable (view counts, a cart that merges both versions), A is the better choice. In an AI agent system, a task board that assigns work to exactly one agent needs C, while a cache of fetched web pages can be served stale.
PACELC: the everyday trade
Daniel Abadi's PACELC (Abadi, 2012) extends CAP: if Partition, choose Availability or Consistency; Else, choose Latency or Consistency. A linearizable write across three regions waits for a remote acknowledgement, a 60 to 150 ms round trip on every write. Partitions are rare; latency is constant, so the ELC half often matters more.
Down the stack
Layer 1The contract
A CP system promises: every acknowledged write is visible to every later read that succeeds, and during a partition, requests that cannot reach a quorum return an error or time out rather than a stale answer. An AP system promises: every request to a live node gets a response, and once communication is restored and writes stop, all replicas converge to the same value (eventual consistency), according to a stated merge rule. Jepsen tests both kinds of contract by injecting partitions and checking the recorded history.
Layer 2The mechanism
In Raft (used by etcd, Consul and CockroachDB), a leader appends each write to its log and commits it only when a majority has persisted the AppendEntries message. A leader on the minority side cannot commit. Plain Raft lets it go on believing it leads until it hears a higher term, so implementations add a quorum check: etcd's raft library steps a leader down automatically when it loses contact with a quorum (etcd raft). Linearizable reads go through ReadIndex, which confirms leadership with a heartbeat round before answering, or through leases, which skip that round but trust every machine's clock. In Dynamo-style stores, each key has N replicas; a write succeeds when W acknowledge and a read when R respond. With R + W > N (for example N=3, W=2, R=2), read and write sets overlap, which gives strong reads only when there are no concurrent writes and no sloppy quorums. Hinted handoff, read repair and Merkle-tree anti-entropy fix divergence later; Cassandra resolves conflicts per cell by write timestamp.
Layer 3The machine
A node cannot tell a partition from a slow peer, and TCP gives up slowly: with Linux's default tcp_retries2 = 15, an established connection can take around 15 minutes to report failure (Linux docs). So systems use their own timeouts: etcd's default heartbeat is 100 ms and its election timeout is 1,000 ms, with the guidance that the election timeout be at least 10 times the round-trip time (etcd tuning); Raft's paper suggests 150 to 300 ms election timeouts inside one data centre (Raft paper). Every write that needs a quorum pays at least one network round trip plus an fsync on a majority (a few hundred microseconds on NVMe, several milliseconds on cloud network disks). Last-writer-wins depends on wall clocks, and NTP-synced clocks typically drift by single-digit milliseconds, so two writes that close together may be ordered arbitrarily and one silently lost.
Why it's built this way
- Messages between machines can be lost or delayed without bound, so no node can know for sure whether its peers saw a write.
- Waiting for confirmation is the only way to be sure, so consistent systems require quorums and refuse to answer without one.
- Quorums cost a round trip on every write, and some users cannot afford to wait or to be refused, so other systems accept writes locally and repair divergence later.
- Repair requires deciding which concurrent write wins, so available systems carry timestamps, version vectors or CRDT metadata.
Diagram
sequenceDiagram
participant C1 as Client A
participant N1 as Node 1 (majority side)
participant N3 as Node 3 (minority side)
participant C2 as Client B
Note over N1,N3: Network partition
C1->>N1: write seat=Ana
N1-->>C1: ok (Nodes 1 and 2 form a quorum)
C2->>N3: read seat
alt CP choice
N3-->>C2: error: no quorum
else AP choice
N3-->>C2: seat=empty (stale)
end
Note over N1,N3: Partition heals: Node 3 catches up or mergesWorked example
A concert ticketing service stores seat assignments in a three-node cluster spread over three availability zones, and user profile data (display name, avatar, favourite artists) in the same cluster.
Zone C is cut off from A and B for 4 minutes during an on-sale with 20,000 seat requests per minute, a third of them routed to zone C.
With seats configured CP (quorum writes, linearizable reads), the node in zone C refuses seat writes. The load balancer's health check sees errors from zone C and shifts traffic to A and B within about 30 seconds. For those 30 seconds, roughly 3,300 requests fail and clients retry. No seat is sold twice.
Had seats been AP (local writes, last-writer-wins on heal), zone C would have kept selling for 4 minutes. If 1% of those roughly 26,700 sales collided with sales of the same seat on the other side, about 270 seats would be double-sold, and last-writer-wins would silently drop one buyer of each. Each needs a refund and an apology, far costlier than 30 seconds of retries.
Profiles are the opposite case. If a user edits their display name on both sides, one version wins on heal, which is acceptable, and CP would only add refusals and a quorum round trip to every update. The team sets profiles to consistency level ONE and seats to QUORUM in the same cluster.
In the wild
- GitHub, 2018: 43 seconds of partition, 24 hours of degradation. Optical maintenance cut the US East hub off from the primary data centre for 43 seconds, and in that window Orchestrator used Raft to promote West Coast MySQL primaries. Writes landed on both coasts, so GitHub chose to keep data consistent over staying fully available: it restored from backups and replayed writes, with service degraded for 24h11m and 954 unreplicated writes to reconcile on one cluster. GitHub planned to stop Orchestrator promoting primaries across regions (GitHub).
- GitHub, 2012: split brain in a Pacemaker cluster. A Pacemaker segfault partitioned the MySQL cluster and two separate clusters ran master elections at once, the worst case of availability without a quorum. MySQL and Redis IDs drifted apart, and 16 private repositories were visible to non-collaborators for 7 minutes; automatic failover of the active role was then changed to need a human (GitHub).
- Google, 2017: how often partitions really matter. In Spanner's internal incident data, network problems were under 8% of incidents, there was never a large set of clusters partitioned from another, and no Spanner quorum ever ended up on the minority side. The two biggest outages were software bugs that hit every replica at once, which is why Brewer calls Spanner technically CP but "effectively CA" (Brewer).
Trade-offs
| Choice under partition | Strengths | Weaknesses | Choose when |
|---|---|---|---|
| Consistency (quorum, refuse minority) | No stale reads, no conflicting writes, simple application logic | Minority side errors, higher write latency | Money, inventory, uniqueness, locks, leader election |
| Availability (accept locally, merge later) | Always answers, low latency, survives region isolation | Stale reads, conflicts, merge logic and its bugs | Counters, carts, social data, caches, offline-first apps |
| Tunable per request | Fit each operation to its need | Easy to misconfigure, harder to reason about | Mixed workloads in one store |
| CRDT-based availability | Automatic, deterministic merges | Limited data types, metadata overhead | Collaborative editing, counters, sets |
Pitfalls in production
- Trusting the label. A database marketed as "CP" may allow stale reads from followers by default, and an "AP" store at
QUORUMmay still lose writes with last-writer-wins. Read the docs for the exact read and write settings you use, and check Jepsen reports. Kleppmann argues the labels are so loose that most systems are neither CP nor AP in the theorem's strict sense (Kleppmann). - Last-writer-wins on skewed clocks. Concurrent writes are ordered by wall clocks that disagree by milliseconds, and one write vanishes. Use version vectors or CRDTs where losing a write matters.
- Quorum math with sloppy quorums. Hinted handoff lets writes land on nodes outside the key's replica set, so
R + W > Nno longer guarantees overlap. Know whether your store does this. - Retries that turn CP into duplicates. A client that times out on a CP write and retries may apply it twice if the first attempt actually committed. Use idempotency keys.
- Forgetting the healing phase. Teams design the partition behaviour and not the reconciliation. Plan and test what happens to conflicting writes when the network returns.
Leadership lens
- Ask for each data type: what is the cost of a stale read and of a lost or conflicting write? That answer, not a database brand, decides C or A.
- Push teams to state consistency settings per operation in the design doc, including the read path, which is where stale reads usually hide.
- Remember PACELC when approving multi-region designs: strong consistency across regions adds a round trip to every write, every day.
Check yourself
Why is "CA" not a meaningful option for a system replicated across data centres?
Show answer
Partitions will happen whether you choose them or not. Calling a system CA only describes its behaviour when the network is healthy; it says nothing about what it does when a partition arrives.
In a 5-node Raft cluster split 2/3, which side can accept writes, and what do clients of the other side see?
Show answer
The 3-node side has a majority and keeps committing writes. Clients of the 2-node side get errors, timeouts or redirects until the partition heals.
N=3, W=1, R=1. Is this CP or AP during a partition, and what can go wrong?
Show answer
It is available, since any single replica can answer. Reads can be stale and concurrent writes on both sides conflict, to be resolved by the store's merge rule.
What does PACELC add that CAP leaves out?
Show answer
The trade during normal operation: strong consistency requires coordination on every write, which costs latency even when there is no partition.
Sources and further study
Primary
- Ongaro and Ousterhout, In Search of an Understandable Consensus Algorithm (Raft): majority commit, leader election and the timing requirement that makes minority sides stop.
- Brewer, Spanner, TrueTime and the CAP Theorem (2017): technically CP, effectively CA, with incident data on how rare partitions are.
Systems docs
- etcd tuning guide: heartbeat and election timeouts and how to size them from round-trip time.
- DynamoDB: How global tables work: the same product offering eventual (last writer wins) and strong multi-Region modes.
- etcd-io/raft README: quorum-loss step-down, ReadIndex-style reads and lease reads.
Books
- Designing Data-Intensive Applications (1st edition), Martin Kleppmann, chapter 9 "Consistency and Consensus": linearizability, the cost of it, and why CAP is a narrow theorem.
- Database Internals, Alex Petrov, chapter 11 "Replication and Consistency": a section on the "infamous CAP" alongside consistency models.
- Understanding Distributed Systems (2nd edition), Roberto Vitillo, chapter 10 "Replication": CAP and PACELC in a practitioner's frame.
Other curricula
- donnemartin/system-design-primer: states CAP as "only two of three" and then says you must tolerate partitions, so the real choice is CP or AP; it does not cover PACELC or per-operation choices.
- roadmap.sh System Design: lists CAP under availability versus consistency next to weak, eventual and strong consistency patterns.
Watch
- L16: The CAP Theorem (Distributed Systems Course): a lecture-style walk through the theorem, its proof and its limits.
- CAP Theorem in 10 Minutes (SCALER): a short refresher on the three properties and the partition trade.
Free resources
- Brewer's Conjecture and the Feasibility of Consistent, Available, Partition-Tolerant Web Services (Gilbert and Lynch): the formal statement and proof.
- CAP Twelve Years Later: How the "Rules" Have Changed (Eric Brewer, InfoQ): Brewer's own correction of the "pick two" reading.
- Please stop calling databases CP or AP (Martin Kleppmann): why the labels mislead and what to say instead.
- Consistency Tradeoffs in Modern Distributed Database System Design (Daniel Abadi): the PACELC paper.
- Consistency models (Jepsen): a map of consistency models and which ones remain available under partition.