CS Fundamentals Trainer · Guides

The CAP theorem and consistency models

What CAP really says, a CP versus AP partition experiment, quorums (R + W > N), the spectrum from linearizable to eventual, and PACELC.

12 min read

The CAP theorem is the most quoted and most misquoted result in distributed systems. The slogan "pick two of consistency, availability, partition tolerance" is wrong in a way that matters. This guide states what the theorem actually says, shows it in a small experiment, and then covers what you really choose between in practice: the consistency models and the latency trade-off that applies even when nothing is broken.

1. What the theorem says

Eric Brewer conjectured it in 2000, and Seth Gilbert and Nancy Lynch proved a precise version in 2002 [1]. Three properties of a replicated service:

The theorem: when the network is partitioned, a system cannot be both consistent and available.

The argument is short. Split two replicas by a partition. A client writes x = 1 to one side. Another client reads x from the other side. That node has two choices: answer with the old value (available, but not consistent: the read missed a completed write) or refuse to answer until it can talk to the other side (consistent, but not available). There is no third option.

2. Why "pick two" is misleading

3. A small experiment

I simulated five nodes holding a shopping cart, and cut the network into a group of two (A, B) and a group of three (C, D, E). Then I wrote to the cart from both sides and read it back. Two modes:

Result:

CP during partition: A (minority side): unavailable, no majority
                     D (majority side): ok   -> reads [milk,bread]
   after heal, the minority catches up: [milk,bread]   (nothing was lost)

AP during partition: A: ok  (cart = [milk,eggs])
                     D: ok  (cart = [milk,bread])      <- two different carts
   after heal + last-write-wins: [milk,bread]          (the other write is lost)

The CP system told the client on the minority side "no", and never showed a wrong cart. The AP system answered everyone, produced two diverging versions, and when the partition healed, one customer's addition (eggs) was silently discarded. That is the trade: an error now, or a conflict to resolve later.

Here is the model (a short Python script you can run as it stands). Each node keeps (version, value) for a key. A partition splits the nodes into groups that cannot talk. In CP mode a group needs a strict majority to read or write; in AP mode every group serves requests from its own nodes, and repair applies last write wins (the highest version) after the heal.

class Cluster:
    def __init__(self, names, mode):
        self.data = {n: {} for n in names}      # node -> {key: (version, value)}
        self.mode, self.groups, self.clock = mode, [set(names)], 0

    def partition(self, *groups): self.groups = [set(g) for g in groups]
    def heal(self): self.groups = [set(self.data)]
    def group_of(self, node): return next(g for g in self.groups if node in g)

    def _need_majority(self, node):
        if self.mode == "CP" and len(self.group_of(node)) <= len(self.data) // 2:
            raise RuntimeError("unavailable: no majority")

    def write(self, via, key, value):
        self._need_majority(via)
        self.clock += 1
        for n in self.group_of(via):             # only the nodes this node can reach
            self.data[n][key] = (self.clock, value)

    def read(self, via, key):
        self._need_majority(via)
        return self.data[via].get(key)

    def repair(self, key):                       # after healing: the highest version wins
        best = max(d[key] for d in self.data.values() if key in d)
        for d in self.data.values(): d[key] = best

for mode in ("CP", "AP"):
    c = Cluster("ABCDE", mode)
    c.write("A", "cart", "[milk]")
    c.partition("AB", "CDE")
    for via, value in (("A", "[milk,eggs]"), ("D", "[milk,bread]")):
        try: c.write(via, "cart", value); print(mode, via, "write ok")
        except RuntimeError as e: print(mode, via, "write refused:", e)
    c.heal(); c.repair("cart")
    print(mode, "after heal:", c.data["A"]["cart"][1])

Running it prints that in CP mode the write through A is refused and the cart ends as [milk,bread], and in AP mode both writes succeed and the cart also ends as [milk,bread], because bread carried the higher version and eggs is gone.

A real AP system can avoid silent loss by keeping both versions and merging them (a shopping cart can be merged by taking the union of items). Dynamo-style stores use version vectors to detect that two writes were concurrent, and CRDTs (conflict-free replicated data types) are data structures designed so that any merge order gives the same result.

4. Quorums: how replicated stores pick the side

Take N replicas of each item. A write must be acknowledged by W of them and a read must ask R of them. If

R + W > N

then every read set and every write set overlap in at least one replica, so a read always contacts a replica that saw the latest acknowledged write. I checked it by brute force for N = 3 and N = 5: for every R and W, "every read set intersects every write set" is true exactly when R + W > N.

NWREffect
322Balanced; tolerates one failed replica for both reads and writes
331Fast reads, writes need every replica
313Fast writes, reads need every replica
311R + W = 2 is not more than 3: no guarantee; fast, may read stale data

Be careful: overlapping quorums are necessary for the guarantee but not sufficient for full linearizability in a real store. Concurrent writes, clock problems, "sloppy" quorums that accept writes on other nodes during a failure, and partial failures during a write can all still produce stale or lost reads. Consensus algorithms (Raft, Paxos) add the missing pieces: a single leader per term, ordered logs, and majority commit.

5. Consistency models: the spectrum

From strongest to weakest. Each model allows more behaviour, and costs less coordination.

ModelWhat a client may observeExample
LinearizableBehaves like one copy; a completed write is seen by every later readA lock service, leader election, a bank balance
SequentialAll see operations in one order, consistent with each client's own order, but not necessarily real timeSome replicated logs
CausalOperations that are causally related (a reply after the message it answers) are seen in order by everyone; unrelated ones may differComments and replies
Read your writes (session guarantee)After I write, my own reads see itProfile edits
Monotonic reads (session guarantee)I never see data go back in timeA feed that does not "un-update"
EventualIf writes stop, all replicas converge to the same value, eventuallyDNS, many caches, Dynamo-style stores with low R and W

The practical middle: session guarantees such as read-your-writes and monotonic reads are cheap (route a user to one replica or remember a version) and remove most of what users notice as "inconsistent". The caching guide's section on replicas shows the same problem from the application side (caching strategies). Do not confuse these with the isolation levels of a database transaction, which are about concurrent transactions on one database (transaction isolation levels); strict serializability combines both ideas.

6. PACELC: what about when nothing is broken?

Daniel Abadi pointed out that CAP only describes the failure case, while the trade-off between consistency and latency exists all the time [3]. His formulation, PACELC: if there is a Partition, choose Availability or Consistency; Else (normal operation), choose Latency or Consistency.

The reason is physics: to be strongly consistent, a write must be acknowledged by replicas that may be in other data centres, so it costs at least one cross-region round trip (tens to hundreds of milliseconds). A system can answer faster by acknowledging after one local replica and replicating in the background, at the price of stale reads. So:

Treat the labels as a way to ask the right question, not as exact classifications of products; vendors' defaults and options change.

7. How to choose

Ask what a wrong answer costs compared with no answer:

NeedLean towardsWhy
Money, inventory counts that must not oversell, unique usernames, leader election, locksConsistency (CP, linearizable)A stale read causes real damage; an error is better
Social feeds, view counters, "likes", product catalogues, recommendations, DNSAvailability (AP, eventual)Slightly stale is fine; unavailable is not
Shopping cartAvailability with a merge ruleLosing a sale from downtime is worse than a merge to resolve
Data users edit themselvesRead your writes at leastPeople notice their own changes disappearing

Also remember that the choice can differ per feature within one product: use a consistent store for payments and an eventually consistent one for activity feeds.

8. Common mistakes

9. Questions interviewers like to add

"Can you have CP and AP at once?"

Per request, yes: the same store may offer strong reads for some calls and fast stale reads for others. For one operation during one partition, no.

"Is a system with a majority leader (Raft) CP?"

Yes in the usual sense: the minority side cannot elect a leader or commit, so it is unavailable for writes, while the majority side stays consistent.

"How do two sides reconcile after a partition?"

Last write wins (simple, loses data), version vectors that keep both and ask the application to merge, CRDTs that merge automatically, or a rule specific to your domain (union of cart items, maximum of a counter).

"Does eventual consistency mean data can be wrong forever?"

No. It means replicas converge if updates stop. The questions are how long "eventually" is (usually milliseconds to seconds) and what the application does with stale reads in the meantime.

10. Things to mention in an interview

Quick recap

  1. CAP: in a partition, a replicated system must choose between linearizable consistency and availability.
  2. Partitions cannot be avoided, so the real choice is C or A during one.
  3. In the simulation, the CP side returned an error and lost nothing; the AP side answered and lost an update.
  4. R + W > N makes read and write quorums overlap, but is not the whole story.
  5. Consistency is a spectrum: linearizable, sequential, causal, session guarantees, eventual.
  6. PACELC: even without partitions, you trade latency against consistency.
  7. Decide per feature by the cost of a wrong answer versus no answer.

Sources

  1. Seth Gilbert and Nancy Lynch, Brewer's conjecture and the feasibility of consistent, available, partition-tolerant web services, ACM SIGACT News, 2002. The proof of the theorem.
  2. Martin Kleppmann, A Critique of the CAP Theorem, 2015. On the ambiguity of CAP's definitions and a delay-sensitivity alternative.
  3. Daniel Abadi, Consistency Tradeoffs in Modern Distributed Database System Design, IEEE Computer, 2012. PACELC.
  4. Eric Brewer, CAP Twelve Years Later: How the "Rules" Have Changed, IEEE Computer, 2012.
  5. Giuseppe DeCandia et al., Dynamo: Amazon's Highly Available Key-value Store, SOSP 2007. Quorums, version vectors, and the shopping cart example.
  6. Diego Ongaro and John Ousterhout, In Search of an Understandable Consensus Algorithm (Raft), 2014.
  7. Kyle Kingsbury, Jepsen: analyses of distributed databases under failures.

Practise this topic: Distributed Systems & Messaging System Design

More guides