> ## Documentation Index
> Fetch the complete documentation index at: https://miliracle.online/llms.txt
> Use this file to discover all available pages before exploring further.

# The CAP Theorem

> When the network between your servers breaks, you must choose: a wrong answer or no answer. A visual guide to CAP, CP vs AP and PACELC.

export const QUIZ = [{
  q: "The cable is cut. A CP system gets a read it cannot check. What does it do?",
  a: ["Returns an error or waits", "Returns its own copy", "Asks the other side anyway"],
  c: 0,
  why: "CP keeps consistency. If a server cannot be sure its answer is the latest, it gives no answer."
}, {
  q: "Why is “CA” not a real option for a distributed database?",
  a: ["It is too slow", "Networks can always fail, so splits will happen", "No database supports SQL and replication"],
  c: 1,
  why: "Once data lives on more than one machine, the link between them can break. You cannot choose to never have a partition."
}, {
  q: "Lisbon answers $100 while Hanoi already has $150. What is that called?",
  a: ["A stale read", "A deadlock", "A lost write"],
  c: 0,
  why: "Lisbon returned an old value. AP systems accept stale reads during a split and catch up later."
}, {
  q: "What happens to MongoDB writes while it elects a new primary?",
  a: ["They go to any node", "They wait or fail until a new primary exists", "They are saved on the client"],
  c: 1,
  why: "MongoDB takes writes only on the primary. With no primary, writes pause. Drivers can retry them once a new primary is elected."
}, {
  q: "PACELC adds a second trade-off. When the network is fine, you choose between…",
  a: ["Consistency and latency", "Availability and cost", "SQL and NoSQL"],
  c: 0,
  why: "Even with no split, waiting for every copy to confirm a write makes it slower. Faster answers mean some copies may lag."
}];

export const pacDot = (lane, t) => {
  if (t >= lane.depart && t < lane.arrive) return (t - lane.depart) / (lane.arrive - lane.depart);
  if (lane.ack && t >= lane.arrive && t < lane.ack) return 1 - (t - lane.arrive) / (lane.ack - lane.arrive);
  return null;
};

export const PAC_LANES = [{
  name: "Answer now",
  tag: "EL: pick latency",
  depart: 5,
  arrive: 5 + 120,
  ack: null,
  ok: 5,
  read: 55
}, {
  name: "Wait for the copy",
  tag: "EC: pick consistency",
  depart: 0,
  arrive: 120,
  ack: 240,
  ok: 245,
  read: 295
}];

export const PAC_T = 320;

export const SERVICES = [{
  name: "Bank ledger",
  pick: "CP",
  why: "If both sides accept a withdrawal, the same money can be spent twice. A short error is cheaper than a wrong balance.",
  other: "AP would risk overdrafts that someone must fix by hand."
}, {
  name: "Shopping cart",
  pick: "AP",
  why: "A cart that refuses “Add to cart” loses a sale. If two copies differ, merge them. At worst a removed item comes back.",
  other: "CP would show errors to shoppers during every network hiccup."
}, {
  name: "Like counter",
  pick: "AP",
  why: "Showing 1,203 likes instead of 1,205 for a few seconds hurts no one.",
  other: "CP would block likes to protect a number nobody checks closely."
}, {
  name: "Username sign-up",
  pick: "CP",
  why: "If both sides accept the name “linh”, two people own it. Asking the user to try again is better.",
  other: "AP would need a messy rename for one of the two users later."
}, {
  name: "Product reviews",
  pick: "AP",
  why: "A review that appears a few seconds late is fine.",
  other: "CP would make the page fail just to show the very latest review."
}, {
  name: "Last seat on a flight",
  pick: "CP",
  why: "Selling one seat twice means a customer at the gate with no seat. Many sellers still choose AP and apologize. It is a business decision.",
  other: "AP would sometimes sell the same seat twice."
}];

export const LANE_EVENTS = [[0, "Both clusters are healthy. Every write succeeds."], [3, "The network splits. MongoDB's primary loses contact with most of its group and steps down. Cassandra keeps accepting writes on both sides."], [5, "MongoDB is electing a new primary. Writes wait or fail and get retried. Cassandra's copies now disagree."], [7, "MongoDB has a new primary on the bigger side. Writes work again."], [9, "The network heals. Cassandra sends each node the writes it missed and repairs the rest. Copies match again."], [11, "Same end state, different bill. MongoDB paid with a pause. Cassandra paid with a window of disagreement."]];

export const TICK_GLYPH = {
  ok: "✓",
  wait: "✕",
  split: "≠",
  fix: "↺"
};

export const LANES = [{
  name: "MongoDB",
  tag: "CP by default",
  ticks: ["ok", "ok", "ok", "wait", "wait", "wait", "wait", "ok", "ok", "ok", "ok", "ok"],
  sum: "Writes stop for ticks 3–6 while a new primary is elected, then resume. Data never disagrees."
}, {
  name: "Cassandra",
  tag: "AP by default",
  ticks: ["ok", "ok", "ok", "split", "split", "split", "split", "split", "split", "fix", "ok", "ok"],
  sum: "Writes never stop, but copies disagree for ticks 3–8 until repair at tick 9."
}];

export const SPLIT = [3, 8];

export const TICKS = 12;

export const TRI_VIEWS = [{
  id: "myth",
  label: "Show the “pick two” view",
  text: "The popular picture: three corners, pick any edge. It makes CA look like a fair choice."
}, {
  id: "real",
  label: "Show the real choice",
  text: "In a distributed system the network can always fail, so P is not optional. CA is off the table. The real question is narrower: while the network is split, do you keep C or A?"
}];

export const MODES = [{
  id: "CP",
  label: "Mode CP: refuse when unsure"
}, {
  id: "AP",
  label: "Mode AP: answer anyway"
}];

export const labStep = (s, mode, act) => {
  const cp = mode === "CP";
  const add = (text, tone) => ({
    n: s.n + 1,
    log: [{
      id: s.n + 1,
      text,
      tone
    }, ...s.log].slice(0, 4)
  });
  if (act === "cut") return {
    ...s,
    link: false,
    base: s.h,
    hOps: 0,
    lOps: 0,
    ...add("Cable cut. Hanoi and Lisbon can no longer talk.", "")
  };
  if (act === "heal") {
    if (!s.hOps && !s.lOps) return {
      ...s,
      link: true,
      ...add("Cable fixed. Nothing to repair: the two copies never disagreed.", "ok")
    };
    const merged = s.base + s.hOps + s.lOps;
    const sum = `${money(s.base)}${s.hOps ? ` + ${money(s.hOps)}` : ""}${s.lOps ? ` − ${money(-s.lOps)}` : ""}`;
    return {
      ...s,
      link: true,
      h: merged,
      l: merged,
      hOps: 0,
      lOps: 0,
      ...add(`Cable fixed. Each side replays the changes it missed: ${sum} = ${money(merged)}. Both show ${money(merged)}.`, "ok")
    };
  }
  if (act === "deposit") {
    if (s.link) return {
      ...s,
      h: s.h + 50,
      l: s.l + 50,
      ...add(`Deposit $50 in Hanoi: accepted and copied to Lisbon. Both show ${money(s.h + 50)}.`, "ok")
    };
    if (cp) return {
      ...s,
      ...add("Deposit $50 in Hanoi: refused. Hanoi cannot copy it to Lisbon, so it says no rather than let the copies disagree.", "bad")
    };
    return {
      ...s,
      h: s.h + 50,
      hOps: s.hOps + 50,
      ...add(`Deposit $50 in Hanoi: accepted. Hanoi shows ${money(s.h + 50)}, Lisbon still shows ${money(s.l)}.`, "warn")
    };
  }
  if (act === "withdraw") {
    if (s.link) return {
      ...s,
      h: s.h - 30,
      l: s.l - 30,
      ...add(`Withdraw $30 in Lisbon: accepted and copied to Hanoi. Both show ${money(s.l - 30)}.`, "ok")
    };
    if (cp) return {
      ...s,
      ...add("Withdraw $30 in Lisbon: refused. Lisbon cannot copy it to Hanoi, so it says no.", "bad")
    };
    return {
      ...s,
      l: s.l - 30,
      lOps: s.lOps - 30,
      ...add(`Withdraw $30 in Lisbon: accepted. Lisbon shows ${money(s.l - 30)}, Hanoi still shows ${money(s.h)}.`, "warn")
    };
  }
  if (s.link) return {
    ...s,
    ...add(`Read balance in Lisbon: ${money(s.l)}. Up to date.`, "ok")
  };
  if (cp) return {
    ...s,
    ...add("Read balance in Lisbon: error. Lisbon cannot check with Hanoi, so it will not risk an old answer.", "bad")
  };
  if (s.l !== s.h) return {
    ...s,
    ...add(`Read balance in Lisbon: ${money(s.l)}. This is stale: Hanoi already shows ${money(s.h)}.`, "warn")
  };
  return {
    ...s,
    ...add(`Read balance in Lisbon: ${money(s.l)}. Right this time, but only by luck: Lisbon could not check.`, "warn")
  };
};

export const money = v => `$${v}`;

export const LAB_START = () => ({
  h: 100,
  l: 100,
  base: 100,
  link: true,
  hOps: 0,
  lOps: 0,
  n: 0,
  log: []
});

export const HERO_PHASES = [{
  up: true,
  h: "$100",
  l: "$100",
  text: "Two copies of one balance, kept in step over a cable."
}, {
  up: false,
  h: "$150",
  l: "$100?",
  text: "The cable breaks. Hanoi takes a $50 deposit. Lisbon is asked for the balance. Old answer, or no answer?"
}, {
  up: true,
  h: "$150",
  l: "$150",
  text: "The cable is fixed. The copies catch up."
}];

export const Cable = ({uid, up, reduce, w = 72}) => <svg className="hdw-ct-cable" viewBox={`0 0 ${w} 40`} aria-hidden="true">
    {up ? <>
        <path id={`ct-wire-${uid}`} d={`M4 20 H${w - 4}`} className="s-acc" />
        {!reduce && [0, 1].map(i => <circle key={i} r="3.5" className="f-acc">
              <animateMotion dur="1.6s" repeatCount="indefinite" begin={`-${i * 0.8}s`} keyPoints={i ? "1;0" : "0;1"} keyTimes="0;1" calcMode="linear">
                <mpath href={`#ct-wire-${uid}`} />
              </animateMotion>
            </circle>)}
      </> : <>
        <path d={`M4 20 H${w / 2 - 10} M${w / 2 + 10} 20 H${w - 4}`} className="s-line" />
        <path d={`M${w / 2 - 6} 14 l12 12 M${w / 2 + 6} 14 l-12 12`} className="s-bad" />
      </>}
  </svg>;

export const useOnceVisible = (ref, onEnter) => {
  const cb = useRef(onEnter);
  cb.current = onEnter;
  useEffect(() => {
    const el = ref.current;
    if (!el) return;
    if (typeof IntersectionObserver === "undefined") {
      cb.current();
      return;
    }
    const io = new IntersectionObserver(entries => {
      if (entries.some(e => e.isIntersecting)) {
        io.disconnect();
        cb.current();
      }
    }, {
      threshold: 0.35
    });
    io.observe(el);
    return () => io.disconnect();
  }, [ref]);
};

export const useReducedMotion = () => {
  const [reduce, setReduce] = useState(false);
  useEffect(() => {
    const mq = window.matchMedia("(prefers-reduced-motion: reduce)");
    setReduce(mq.matches);
    const on = e => setReduce(e.matches);
    mq.addEventListener("change", on);
    return () => mq.removeEventListener("change", on);
  }, []);
  return reduce;
};

> This is the Markdown version of an interactive page. Every animated figure on the web page is replaced here by a table or list with the same facts. Source: adapted from IBM's ["What is the CAP theorem?"](https://www.ibm.com/think/topics/cap-theorem), with corrections.

Your bank keeps your balance on two servers, one in Hanoi and one in Lisbon. If one building burns down, the other still has your money. Every change in one city is copied to the other over a network cable.

One day the cable breaks. You deposit \$50 in Hanoi. A minute later, your partner checks the balance from Lisbon. Lisbon has not heard about the deposit. What should it say?

It has only two honest options. It can show the old balance, which is wrong. Or it can show an error, which is useless. There is no third option that is both correct and immediate. That trade-off has a name: the **CAP theorem**.

<Tip>
  **In short**

  * A distributed system keeps copies of data on several machines that talk over a network.
  * Networks fail. When the machines cannot talk, the system is *partitioned*.
  * During a partition, each request must either get a possibly wrong answer (keep **A**vailability) or wait or fail (keep **C**onsistency).
  * "Pick two of three" is misleading. Partitions are not optional, so the real choice is C or A, and only while the network is split.
  * Most real databases let you tune this choice. Many choose per request.
</Tip>

## Three promises

Eric Brewer proposed the idea in a talk in 2000. Seth Gilbert and Nancy Lynch proved it in 2002. It talks about three promises a system with copies on several servers might make.

* **Consistency (C).** Every read sees the latest write, no matter which server answers. If Hanoi accepted the \$50, Lisbon must not say \$100. Formally this is called *linearizability*. It is **not** the "C" in ACID, which is about following database rules like foreign keys.
* **Availability (A).** Every request to a working server gets a real answer, not an error, even if other servers are cut off.
* **Partition tolerance (P).** The system keeps running when messages between servers are lost or delayed. A **partition** is that break: two groups of servers that cannot reach each other.

The theorem says: while a partition is happening, no system can keep both C and A. Try it yourself in Figure 1.

Start: Hanoi and Lisbon both show \$100. Then the cable is cut.

| Request during the split | CP system (refuse when unsure) | AP system (answer anyway) |
| - | - | - |
| Deposit \$50 in Hanoi | Refused: Hanoi cannot copy it to Lisbon | Accepted: Hanoi \$150, Lisbon still \$100 |
| Read balance in Lisbon | Error: Lisbon cannot check with Hanoi | \$100, which is stale |
| Withdraw \$30 in Lisbon | Refused | Accepted: Lisbon \$70, Hanoi \$150 |
| Cable fixed | Nothing to repair; copies never differed | Copies must be merged. Replaying both changes gives \$100 + \$50 − \$30 = \$120 |

With the cable up, every request succeeds and both copies always match.

The CP side gives up answers to stay correct. The AP side stays open and pays later, when the copies must be merged. In this figure the merge replays both changes. Many real AP systems use a simpler rule called **last write wins**, which keeps only the newest value and can silently drop the other change. The AP side gives **eventual consistency**: once the network heals and repair runs, all copies agree again.

## Why "pick two of three" misleads

CAP is often drawn as a triangle with the advice "pick any two". That makes three options look equal: CP, AP and CA. They are not.

| Popular view | What actually holds |
| - | - |
| Pick any two of C, A, P | P is not optional for data on more than one machine |
| CA is a valid choice | CA only exists on a single machine, which is not distributed |
| The choice is permanent | The choice only matters while a partition is happening |
| A system is either CP or AP | Many databases let you choose per request or per table |

A system with copies on two machines cannot promise that the cable never breaks. Cables get cut, switches reboot, and cloud regions lose contact. So "giving up P" is not a real choice. A database that claims to be CA either runs on one machine or quietly picks C or A when a partition finally comes.

Two more details matter:

* **The choice only applies during a partition.** When the network is healthy, a system can be both consistent and available. Brewer made this point himself in 2012: the "2 of 3" framing was always misleading.
* **The choice is not all-or-nothing.** One system can refuse some requests and answer others. It can also pick differently for different data.

## CP and AP in real databases

Two popular databases make opposite default choices. **MongoDB** leans CP. **Cassandra** leans AP. Figure 3 hits both with the same network split.

Illustrative timeline, one write per tick. The network splits at tick 3 and heals at tick 9.

| Tick | MongoDB (CP by default) | Cassandra (AP by default) |
| - | - | - |
| 0–2 | Writes accepted; copies agree | Writes accepted; copies agree |
| 3 | Primary loses contact with most of its group and steps down | Writes still accepted on both sides |
| 3–6 | No primary; writes wait or fail and get retried | Writes accepted; copies disagree |
| 7 | New primary elected on the bigger side; writes resume | Writes accepted; copies still disagree |
| 9 | Network heals; old primary rejoins as a copy | Missed writes are replayed and repaired; copies agree |
| 10–11 | Normal | Normal |

MongoDB paid with a pause. Cassandra paid with a window in which copies disagreed.

**MongoDB** stores data in a *replica set*: one **primary** that takes all writes, plus copies called secondaries. If the primary cannot reach most of the set, it steps down. The servers on the bigger side elect a new primary. MongoDB's docs say this usually takes no more than about 12 seconds with default settings. During that time writes cannot be accepted. Reads can still be served from secondaries if the app allows it, but they may be stale.

**Cassandra** has no primary. Any copy can accept a write. During a split, both sides keep working, and their copies drift apart. When the network heals, Cassandra fixes the gaps: a node replays writes it held for the missing node (*hinted handoff*), reads compare copies and fix them (*read repair*), and an operator can run a full `nodetool repair`.

These are defaults, not fixed labels. Both systems let each request choose:

* Cassandra's **`QUORUM`** level requires a majority of copies to answer. Use it for both reads and writes, and every read sees the latest write. The price: the minority side of a split now returns errors, just like a CP system.
* MongoDB's **write concern** and **read preference** settings go the other way. You can wait for fewer copies to confirm a write, or read from secondaries that may lag. That trades some consistency for speed and availability.

## Choosing per service

In a microservice design, each service usually owns its own database. That means CAP is not one decision for the whole company. Each service asks its own question: during a split, is a **wrong answer** or **no answer** worse?

| Service | Pick | Why | Cost of the other choice |
| - | - | - | - |
| Bank ledger | CP | Two sides accepting withdrawals can spend the same money twice | AP risks overdrafts fixed by hand |
| Shopping cart | AP | Refusing "Add to cart" loses a sale; merge differing carts later | CP shows errors on every network hiccup |
| Like counter | AP | A count a few seconds behind hurts no one | CP blocks likes to protect an unimportant number |
| Username sign-up | CP | Two people must not own the same name | AP needs a messy rename later |
| Product reviews | AP | A review appearing a few seconds late is fine | CP fails the page to show the newest review |
| Last seat on a flight | CP | Selling one seat twice strands a customer; some sellers still choose AP and apologize | AP sometimes sells a seat twice |

Relational databases like PostgreSQL do not escape this. Once you copy a PostgreSQL database to another server, the same question applies. With *synchronous* replication, a write waits until the copy confirms it, so a split blocks writes. That behaves like CP. With *asynchronous* replication, writes do not wait, so a failover can serve old data or lose recent writes.

## When the network is fine: PACELC

CAP only covers the moments when the network is split. Those are rare. The rest of the time, every write still forces a choice when it reaches Hanoi:

* **Answer now** and copy to Lisbon in the background. The app hears OK fast, but for a moment Lisbon is behind and can give a stale answer.
* **Wait for the copy.** Hanoi says OK only after Lisbon confirms. Every read is correct, but each write pays a round trip between the two cities.

Illustrative timeline. The network works. One-way Hanoi → Lisbon takes 120 ms. Your partner reads in Lisbon 50 ms after you see OK.

| Policy | Copy reaches Lisbon | App sees OK | Partner's read in Lisbon |
| - | - | - | - |
| Answer now (EL) | 125 ms | 5 ms | At 55 ms: \$100, stale |
| Wait for the copy (EC) | 120 ms | 245 ms (after Lisbon's reply returns) | At 295 ms: \$150, correct |

Daniel Abadi named this in 2012 as **PACELC**. Read it as two questions:

1. **If there is a Partition** (P): choose **Availability** (A) or **Consistency** (C). This is CAP.
2. **Else**, when the network is fine (E): choose **Latency** (L) or **Consistency** (C). This is Figure 5.

A database gets a two-part label, one answer per question:

| Class | During a partition | Normal operation | Typical examples |
| - | - | - | - |
| PA/EL | Stay available | Answer fast; copies may lag | Cassandra, DynamoDB with default settings |
| PC/EC | Stay consistent | Wait for copies to agree | HBase, Google Spanner |
| PA/EC | Stay available | Wait for copies to agree | MongoDB in Abadi's original paper |

As with CAP, these labels describe defaults. Tunable settings move most databases between classes.

## Check yourself

Questions this document answers:

* What does the CAP theorem say? During a network partition, a distributed system cannot guarantee both consistency and availability.
* What does a CP system do during a partition? It refuses or delays requests it cannot answer correctly.
* What does an AP system do during a partition? It answers every request, possibly with stale data, and repairs copies later.
* Why is CA not a real option for a distributed database? Partitions cannot be ruled out once data lives on more than one machine.
* Is consistency in CAP the same as in ACID? No. CAP consistency means every read sees the latest write (linearizability). ACID consistency means transactions follow the database's rules.
* What happens to MongoDB writes during a primary election? They wait or fail until a new primary is elected, usually within about 12 seconds by default.
* How does Cassandra repair copies after a partition? Hinted handoff, read repair, and anti-entropy repair (`nodetool repair`).
* Is replicated PostgreSQL CA? No. Synchronous replication behaves like CP; asynchronous replication can serve stale data or lose writes on failover.
* What does PACELC add? When there is no partition, a system still trades latency against consistency.

## Glossary

| Term | Meaning |
| - | - |
| Distributed system | Several machines that work together and talk over a network. |
| Replica | One copy of the data on one machine. |
| Partition | A network break that splits servers into groups that cannot reach each other. |
| Consistency (CAP) | Every read sees the latest write, whichever server answers. Also called linearizability. |
| Availability (CAP) | Every request to a working server gets a non-error answer. |
| Partition tolerance | The system keeps running when messages between servers are lost. |
| Stale read | A read that returns an older value than the latest write. |
| Eventual consistency | If writes stop, all copies become equal after some time. |
| Last write wins | A merge rule that keeps the newest value and drops the others. |
| Primary | The one server in a group that accepts writes, as in a MongoDB replica set. |
| Election | How the remaining servers pick a new primary after the old one is lost. |
| Quorum | A majority of copies. Requiring it for reads and writes keeps them consistent. |
| Hinted handoff | Cassandra holding writes for an unreachable node and replaying them later. |
| PACELC | CAP plus the normal-time trade-off between latency and consistency. |

## For AI agents

You are reading the Markdown export of `/writing/database/cap-theorem`. The web page has the same text plus interactive figures; nothing factual is missing here. The site index for agents is at `/llms.txt`. A related page, `/writing/database/what-is-a-database`, covers databases in general.

***

Adapted from IBM, ["What is the CAP theorem?"](https://www.ibm.com/think/topics/cap-theorem), with corrections: CAP is a choice made during partitions rather than "any two of three"; CAP consistency is not ACID consistency; replicated PostgreSQL is not CA; and MongoDB and Cassandra are CP and AP only by default. Further reading: Eric Brewer, ["CAP Twelve Years Later"](https://www.infoq.com/articles/cap-twelve-years-later-how-the-rules-have-changed/); MongoDB, ["Replica Set Elections"](https://www.mongodb.com/docs/manual/core/replica-set-elections/). Figures are simplified teaching models; timings in Figure 3 are illustrative.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.