Data & System Architecture, from the ground up Lesson 11 / 80

PACELC: what CAP missed

Daniel Abadi's extension. Even in the absence of partitions, you trade latency for consistency.

The previous lesson left CAP in a slightly unflattering position. The theorem is correct, it has a precise statement, and it does describe something real: in the presence of a network partition, you must choose between consistency and availability. But it is silent on the case that occupies almost all of your system’s time, which is the case where there is no partition. Your distributed database is in the partitioned state for a few seconds a week, on a bad week. The other 99.99% of the time, CAP says nothing about it.

Daniel Abadi noticed this in 2010 and gave us the framework that fills the gap. He called it PACELC. The acronym is ugly and the idea is clean. Let me state it once and then unpack it:

PACELC: if Partition, choose Availability or Consistency; Else, choose Latency or Consistency.

The first half (PAC, “if Partition, A or C”) is exactly CAP. The second half (ELC, “Else, L or C”) is the part that CAP did not name. It says: even when nothing is broken, even on a perfectly healthy network, even on a Tuesday afternoon with no incidents in flight, you are still trading latency against consistency on every write to a replicated system. That trade-off does not show up in CAP because CAP is only about the partitioned case. PACELC drags it into the open.

Why the everyday case has its own trade-off

To see why the ELC trade-off exists, picture a strongly consistent database with replicas in three regions: London, Frankfurt, and Singapore. Strong consistency means that any read, from any region, returns the most recent committed write. To honour that promise, the system has to make sure that a write is durably acknowledged by a quorum of replicas, including at least some in regions other than the one accepting the write. That means every write incurs at least one cross-region round trip.

A cross-region round trip is expensive. London to Frankfurt is around fifteen milliseconds. London to Singapore is closer to a hundred and seventy. Even if your application is doing nothing else, every write costs you fifteen to two hundred milliseconds of unavoidable wall-clock time, paid into the consistency tax.

Now imagine the alternative. The same system, but the operator opts for “eventually consistent” writes. The write is acknowledged as soon as the local replica has it durably stored. Replication to the other regions happens asynchronously in the background. The latency drops from a hundred and seventy milliseconds to around two. The cost is that, for some short window, a read from Singapore may not yet see the write that London just acknowledged.

That is the ELC trade-off. The network is not partitioned. Everything is healthy. You are still paying, on every write, in either latency or consistency. You do not get to opt out. You only get to choose.

The four-quadrant classification

PACELC gives every distributed database a two-letter code. The first letter is the partition behaviour: PA (available during partition, possibly stale) or PC (consistent during partition, possibly unavailable). The second letter is the no-partition behaviour: EL (low latency, possibly stale) or EC (consistent, with the latency cost).

Combining them gives four quadrants. Most real systems sit in one of two of them.

flowchart TD
    A[PACELC quadrants] --> Q1[PA / EL]
    A --> Q2[PC / EC]
    A --> Q3[PA / EC]
    A --> Q4[PC / EL]
    Q1 --> Q1E[Cassandra default, DynamoDB default, Riak, S3, MongoDB default]
    Q2 --> Q2E[Spanner, FaunaDB, CockroachDB, sync-replicated relational]
    Q3 --> Q3E[Rare in pure form: trades availability for latency wins]
    Q4 --> Q4E[Rare in pure form: trades consistency only on partition]

PA/EL: available, fast, possibly stale

PA/EL systems are optimised for high availability and low latency. They serve writes locally, replicate asynchronously, and accept that two clients can momentarily see different values. Cassandra at default consistency settings, Riak, MongoDB at default settings, and DynamoDB without strongly-consistent reads are all PA/EL.

These systems are the right fit for workloads where staleness is cheap and downtime is expensive. Activity feeds, content recommendations, session storage, large-scale analytics ingestion, IoT telemetry. Any case where “the answer might be a few seconds out of date” does not matter, but “the system is unavailable for a minute” loses you money.

PC/EC: consistent, on both ends, slower

PC/EC systems pay the latency cost of consistency on every write, and refuse during partitions to preserve correctness. Spanner is the textbook example. Google built it specifically to give cross-region strong consistency, using GPS-and-atomic-clock-synchronised TrueTime to bound clock skew, and then accepting that every write costs the global round-trip time.

CockroachDB and FaunaDB are open-ish-source descendants of the Spanner idea. Traditional relational databases with synchronous replication (Postgres with synchronous_commit = remote_apply and a synchronous standby in another zone) sit in the same quadrant.

PC/EC systems are the right fit for workloads where consistency is non-negotiable and the latency budget can absorb tens of milliseconds per write. Financial ledgers, inventory systems where over-selling is a real cost, regulatory data where audit correctness matters more than throughput. The Spanner argument was always that, at scale, you would rather take the latency hit than build correctness on top of an eventually-consistent store yourself.

PA/EC and PC/EL: the rare quadrants

In their pure forms, the other two quadrants are rare. PA/EC is a system that gives up consistency under partition but insists on consistency outside of one. That is hard to build correctly because the partition window will produce divergent state that the consistent-mode reads then have to reconcile. PC/EL is the inverse: refusing under partition, but eventually consistent outside of one. That is also rare because if you are willing to refuse on partition for correctness, you usually want consistency the rest of the time too.

Tunable systems blur the quadrants. Cassandra at consistency level QUORUM behaves much closer to PC/EC for the operations using it, while remaining PA/EL for the operations using ONE. DynamoDB lets each call pick. So the quadrant chart is the architectural starting point; the per-operation choice is the actual lived experience.

A worked example: a session store

Let’s make this concrete. You are designing the session store for a logged-in web application. Sessions are read on every request and updated when the user does anything that should bump their last-active timestamp. Two questions to answer:

  1. What happens on partition? If the session store is partitioned, do you refuse logins (PC) or do you serve a possibly-stale session (PA)?
  2. What happens on the everyday case? Do reads cost a cross-region round trip (EC), or do they read from a local replica (EL)?

For most session stores, the answer is PA/EL. A stale session is fine for a few seconds (the user remains logged in, the last-active timestamp is slightly behind reality). Refusing all logins during a partition is much worse than serving from a stale replica. And the read latency budget on every page load is tight enough that paying for cross-region consistency on each read is a poor trade.

So the implementation is, say, DynamoDB with eventually-consistent reads, or Cassandra with consistency level ONE, or Redis with async replication. Lower cost, lower latency, occasional staleness, available during the inevitable little partitions.

Now contrast with a banking ledger. Same two questions:

  1. On partition, what should happen to a withdrawal? Refuse (PC). Better an error than two withdrawals from the same hundred dollars.
  2. On the everyday case, can a read from another region be stale? No (EC). The ledger has to be linearizable so that every account sees the same balance.

So the implementation is Spanner, FaunaDB, or a synchronously-replicated Postgres cluster across two AZs. Higher latency, higher cost, refuses during partition, never lies.

The two systems sit in opposite PACELC quadrants. They are both correct. Each is correct for its workload.

The per-operation question

The most useful way to apply PACELC is not to classify a system but to classify each operation. A single application typically has both kinds of operations.

A login flow needs PC for the credential check (better refuse than authorise the wrong user) and EC for the read of the user’s role and permissions (these had better not be stale). Cost: a few extra milliseconds, and an error rather than a hang during a partition. Acceptable.

A “show recent activity” feed on the same user’s dashboard can happily be PA/EL. If the feed is one second out of date, nobody dies. If the dashboard refuses to load during a small partition, your users are angrier than if it shows slightly old data.

A purchase flow with payment authorisation needs PC for the charge (do not double-charge under partition) and EC for the read of the cart total (do not let the user pay an old amount). The post-purchase confirmation email can be PA/EL.

The point is that “what database should we use” is the wrong level to ask the question. The right level is “what does each operation need,” and from there you either pick a tunable database that lets you express both modes, or you pick two databases (a strong-consistency primary for the critical operations, an eventually-consistent cache or replica for the rest), or you accept the latency cost of running everything on a PC/EC system because the operational simplicity is worth more than the latency savings.

A reading guide for the four quadrants

If you only know each system by reputation, the quadrant placement helps you ask better questions during evaluation. A short reading guide:

  • Cassandra and Riak are PA/EL by default. Their selling point is staying up. Tunable up to PC/EC for individual operations using QUORUM or ALL consistency levels, but the operational cost goes up with the consistency.
  • DynamoDB is PA/EL by default and supports per-call strongly-consistent reads (which move that call to PC/EC). Auto-scaling and fully-managed, which is its real selling point.
  • MongoDB is PA/EL by default in single-replica reads, and PC/EC if you read from the primary with a majority write concern. The defaults have caused some genuinely embarrassing public incidents.
  • Spanner, CockroachDB, FaunaDB are PC/EC. Strong consistency is the headline feature; latency is the cost. Use when correctness across regions is the requirement.
  • A traditional Postgres or MySQL with synchronous replication to one standby is PC/EC at the small scale (one primary, one synchronous replica, both in the same region). Add cross-region async replicas and you get PA/EL on those replicas, which is fine as long as you read from them only when staleness is acceptable.

The names move around as defaults change between versions, so the durable advice is: ask “what does it do during a partition” and “what does it do on a healthy network”, and write the answers down. PACELC just gives you the four boxes to write them in.

What “consistent” really means

The word “consistent” has been doing heavy lifting in this lesson and the previous one. CAP and PACELC both treat it as a binary: linearizable or not. In real systems, “consistent” is a spectrum, with at least four named points: linearizable, sequential, causal, and eventual. Each has a different cost and a different set of guarantees. A system that is “eventually consistent” is doing very different things from one that is “causally consistent,” and the difference matters for what your application sees.

Lesson 12 unpacks that spectrum, with examples of which guarantee maps to which kind of bug. By the end of it, you will be able to read a database documentation page that says “we provide read-your-writes consistency within a session, and eventual consistency across sessions” and know what that buys you, what it costs you, and which application bugs it does and does not protect you from.

After that, the rest of Module 2 turns toward the patterns and protocols that put these trade-offs into practice: replication strategies, quorum systems, conflict resolution, and the central role of consensus algorithms in any system that wants to make CP-style guarantees. PACELC gives you the menu. The rest of the module is about what each item on the menu actually does to your system.

Citations and further reading

  • Daniel Abadi, “Problems with CAP, and Yahoo’s little known NoSQL system” (2010), the original blog post that introduced PACELC. Reproduced at http://dbmsmusings.blogspot.com/2010/04/problems-with-cap-and-yahoos-little.html (retrieved 2026-05-01).
  • Daniel Abadi, “Consistency Tradeoffs in Modern Distributed Database System Design”, IEEE Computer, 2012. The peer-reviewed write-up of PACELC with the four-quadrant classification of the major systems of the time.
  • James C. Corbett et al., “Spanner: Google’s Globally Distributed Database”, OSDI 2012. The canonical PC/EC system, with the TrueTime mechanism that makes cross-region linearizability tractable.
  • Apache Cassandra documentation on tunable consistency levels.
  • Amazon DynamoDB Developer Guide, sections on consistent reads and global tables. Retrieved 2026-05-01.
  • Kyle Kingsbury’s Jepsen series at https://jepsen.io/. Empirical testing of what databases actually do under partition. The single best resource for cross-checking vendor consistency claims against observed behaviour.
Search