Backend / Databases / SQL / 15_cap_theorem_acid_base.md

CAP, ACID, BASE, PACELC

Updated 6 interview angles 5 min read source
On this page9
  1. CAP
  2. What CAP isn’t
  3. PACELC — the missing piece
  4. ACID vs BASE
  5. Examples
  6. Eventual consistency in practice
  7. Linearizability vs serializability
  8. Choosing your model
  9. Interview angle

CAP, ACID, BASE, PACELC

Vocabulary for talking about distributed-system trade-offs.

CAP

Pick two of three when a network partition occurs:

  • Consistency — every read sees the most recent write (linearizability).
  • Availability — every request gets a non-error response.
  • Partition tolerance — system keeps working when network drops messages between nodes.

The trick: P isn’t really optional. Networks partition. So in practice:

  • CP system — under partition, refuses requests on the minority side to keep consistency. (Postgres single-primary, Spanner, ZooKeeper, etcd, MongoDB with majority writes.)
  • AP system — under partition, both sides keep accepting requests; reconciles later. (Cassandra default, DynamoDB, Riak, CouchDB.)

CA (no partition tolerance) means “single machine” — not really distributed.

What CAP isn’t

  • It’s not a steady-state choice. When the network is healthy, CP systems are also available. The trade-off only fires under partition.
  • It’s not a binary. Real systems offer tunable consistency: e.g., Cassandra QUORUM reads from a majority of replicas (more consistent), ONE from any (more available).

The dial is per query, not per database:

sql
-- AP end: any replica answers, may be stale.
CONSISTENCY ONE;
SELECT balance FROM accounts WHERE id = ?;

-- CP end: R + W > N, so a read sees the last write.
CONSISTENCY QUORUM;

The rule is R + W > N. With three replicas, writing to two and reading from two guarantees an overlap, so the read touches at least one node that has the write. Set both to ONE and you have saved a round trip and given up the guarantee — which is the correct choice for a view counter and the wrong one for a balance.

PACELC — the missing piece

CAP only describes partition behavior. PACELC adds: even when there’s no Partition, you choose between Latency and Consistency.

If Partition: choose Availability or Consistency. Else: choose Latency or Consistency.

Most distributed systems trade some consistency for latency in normal operation: replicating async (low write latency, stale reads possible) vs sync (high latency, fresh reads). Cassandra is PA/EL by default; Spanner is PC/EC.

ACID vs BASE

ACID (transactional databases):

  • Atomicity, Consistency, Isolation, Durability.
  • Strong guarantees, lower throughput in distributed settings.

BASE (eventually-consistent stores):

  • Basically Available
  • Soft state — state can change without input
  • Eventually consistent — given no new updates, all replicas converge

BASE was coined as a deliberate antonym to ACID by Eric Brewer’s group. The point: when scale forces you to give up ACID, name what you actually have.

Examples

System CAP ACID-y?
Postgres (single) CA / CP under replication ACID
MySQL same ACID (with InnoDB)
Spanner CP (TrueTime atomic clocks make wide-area linearizability practical) ACID
CockroachDB, YugabyteDB CP ACID across shards
MongoDB (majority write concern) CP ACID since 4.0 (multi-doc transactions)
DynamoDB tunable; eventually-consistent reads default BASE; transactions optional
Cassandra AP default, tunable BASE
Riak AP BASE
etcd, ZooKeeper, Consul CP (Raft / Zab consensus) strongly consistent metadata

Eventual consistency in practice

In an AP system, reads can be stale. To bound the staleness:

  • Read repair — when a read sees inconsistent replicas, reconcile in the background.
  • Hinted handoff — node A is down; node B holds writes destined for A and replays when A recovers.
  • Anti-entropy — periodic background gossip to reconcile divergent replicas (Merkle trees in Cassandra).
  • Vector clocks — track causality so concurrent updates can be detected (Riak).
  • CRDTs — data types that mathematically converge (counters, sets) without coordination.
  • Last-write-wins — simplest, but loses concurrent updates.

Linearizability vs serializability

Two different “strong consistency” guarantees often conflated:

  • Linearizability — single-object: every read returns the most recent write, in real-time order.
  • Serializability — multi-object: result of concurrent transactions equals some serial order (not necessarily real-time).
  • Strict serializability — both. What ACID + linearizable gives you. What Spanner provides.

A system can be serializable without being linearizable (PostgreSQL serializable mode does not guarantee real-time ordering across separate transactions). And linearizable without being serializable (single-key linearizable KV store).

Choosing your model

Cheat-sheet for “what consistency do I need”:

Use case Need
Bank balances, payments Linearizable + serializable
Multi-row business invariants Serializable
User profile updates Read-your-writes + monotonic reads (session consistency)
Likes / view counts Eventually consistent + counter CRDT
Activity feed Eventually consistent (reorder fine within a few seconds)
Analytics aggregates Eventual; precision/recency tradeoff explicit

Most apps need strong consistency for some data, eventual consistency for the rest. Putting everything in a single CP store is overkill; putting everything in AP is asking for incidents.

Session consistency is the row people skip and the one that fixes the most visible bug: a user saves their profile, the read goes to a lagging replica, and their change appears to have vanished. Route them to the primary until the replica has caught up:

python
async def read_profile(user_id, wrote_at):
    lag = await replica.replication_lag()
    conn = primary if wrote_at > now() - lag else replica
    return await conn.fetch_profile(user_id)

Read-your-writes for the one user who wrote, eventual consistency for everyone else. That is almost always the right split, and it costs one comparison.

Interview angle 6

  • “What is CAP and which two should you pick?” — partition tolerance is not optional in a distributed system, so the real choice is CP or AP under partition. When the network is healthy a CP system is also available; the trade only fires during a partition.
  • “Is it really a binary choice?” — no. Real systems tune per query: with N replicas, R + W > N guarantees a read overlaps the last write. A view counter reads at ONE, a balance reads at QUORUM, in the same database.
  • “ACID or BASE?” — strong guarantees with lower distributed throughput, versus availability with eventual convergence. Note that the C in ACID (constraints hold) is not the C in CAP (replicas agree).
  • “What’s PACELC and why was it needed?” — CAP only describes partition behaviour. PACELC adds the steady-state choice: Else, Latency or Consistency. Async replication buys write latency and permits stale reads; sync replication does the reverse.
  • “Linearizable or serializable?” — single-object real-time ordering versus multi-object equivalence to some serial order. Postgres serializable mode is not linearizable, and a single-key store can be linearizable without being serializable.
  • “A user updates their profile and the change disappears. Why?” — the read hit a lagging replica. Session consistency: route that user to the primary until replication lag has passed, and leave everyone else on replicas.

See Transactions and isolation levels for ACID isolation in single-DB context, Sharding and Partitioning for distributed implications.