The Distributed-Systems Trade-off Hiding in Your p99 (CAP Theorem Never Diagrams It)
Day 4: PACELC — the trade-off you're already making, right now, not during some hypothetical outage
CAP only cashes out when the network partitions — for most teams that's a once-a-quarter event, if that. PACELC is the bill you're paying on every request that isn't a partition. Open your p99 dashboard right now. That number is PACELC, not CAP.
The hook: a config tuned for an event that never happened
Early at PhoenixDX, we ran agent session state in MongoDB with w:majority and readConcern:majority. The reasoning at design time sounded airtight: if a primary dies mid-write, we don't want the next tool call reading a stale agent memory. So we tuned for that partition. It has fired exactly zero times in production. What it does do, every single time — partition or not — is force every agent-turn write to sit and wait for acknowledgment from a majority of replicas before the call returns. An extra network round trip, on every request, forever. We'd designed for the 0.1% case and quietly billed the other 99.9% for it.
Day 3 recap — and the thing CAP never tells you
CAP says: when the network partitions, pick Availability or Consistency, you don't get both. True, and useful — but it only describes the partition. It has nothing to say about what your system does the rest of the time, when every node can see every other node and nothing is on fire. CAP is silent on the 99.9% branch. And for almost every system, that silent branch is the one actually setting your latency, your cost, and what your users experience.
PACELC: the 'else' CAP forgot to diagram
Daniel Abadi's PACELC adds the branch CAP never draws: if there's a Partition, choose Availability or Consistency — same call as CAP. Else, choose Latency or Consistency. It's a plain if/else, and most engineers only ever read the if.
if Partition:
choose Availability OR Consistency # CAP — rare, dramatic, gets the whiteboard
else:
choose Latency OR Consistency # PACELC — constant, invisible, gets the p99That else-branch trade-off exists because replication costs something even when nothing's broken. Want every replica to agree before you ack a write or serve a read? You wait for that agreement — that's the consistency side. Want an answer immediately from whichever replica is closest? Skip the wait, and accept you might hand back something slightly stale — that's the latency side. No partition needed. This is happening on every request, all day, every day.
The four quadrants, as they actually show up
- ▹PA/EL — Partition: choose Availability. Else: choose Latency. This is home turf for Dynamo and Cassandra on default settings: answer fast, reconcile later, treat a stale read as the normal cost of doing business.
- ▹PC/EC — Partition: choose Consistency. Else: choose Consistency. MongoDB on majority write/read concern and HBase live here: correctness comes first, and latency pays for it on every call, partition or not.
- ▹PA/EC and PC/EL — technically possible, almost never seen, because they'd require a system to be sloppy under partition but strict when healthy, or the reverse. Real systems cluster on the PA/EL or PC/EC diagonal, which is exactly why naming a system's quadrant tells you its whole personality in one line.
The AI-era version: your agent stack already picked a quadrant, you just didn't vote
You don't need a distributed database to run into this. Every agent framework holding conversation or session state, every vector store behind a RAG pipeline, every shared queue coordinating a fleet of agents has already made a PACELC choice — usually by default, not by decision. A semantic cache that serves a 'good enough' cached embedding instead of waiting on the freshest index update is PA/EL: fast, stale-tolerant, and fine, because a slightly outdated cache hit rarely wrecks a conversation. An agent's tool-call ledger — the record of 'agent X charged card Y for $Z' — needs to be PC/EC: every reader has to see the same committed state, and you pay the round trip for that. Same stack, two data classes, two different correct answers. The failure isn't choosing EC or EL. It's choosing one for everything and never noticing you did.
Run this audit against your own config today
Forget the partition. Ask what your system does when nothing is wrong — that's the config that's actually charging you rent.
- ▹Find your default write concern / consistency level — Mongo writeConcern, Cassandra CL, DynamoDB strong-vs-eventual reads, your vector DB's index refresh mode. Most teams haven't looked at this since initial setup.
- ▹Ask: does a healthy-path write or read wait on acknowledgment from more than one node before it returns? If yes, you're paying the EC tax on every call, not just during incidents.
- ▹Check your cross-region replication mode — synchronous, you wait; asynchronous, you don't. This is usually the single biggest latency line item hiding inside a 'consistency' setting.
- ▹Classify the data. Money path — financial, auth, an agent's billing/tool-call ledger — needs EC. Vanity path — like counts, view counts, chat scroll history, cache hits — EL is usually the honest answer.
- ▹If you can't say what happens to a piece of data on an ordinary Tuesday at 2pm, you don't know what you configured. You know what you configured for the outage you were imagining.
The tax, quantified
Choosing EC isn't free, and the bill shows up in your p99, not your p50. A local, single-replica ack is roughly one network hop. A majority/quorum ack inside one availability zone adds another cross-node round trip on top — call it low single-digit milliseconds. Run that same wait across regions and you're adding tens of milliseconds, sometimes well over a hundred, because now you're bound by inter-region network latency, not disk or CPU. That tax is invisible in a demo and brutal in aggregate: it lands on every write, every day, compounding across every hop in a multi-agent call chain where each handoff might touch that same EC-tuned store.
- ▹Worth paying: financial ledgers, inventory counts, anything where two readers disagreeing is a compliance or money problem, an agent's audited tool-call or billing trail.
- ▹Not worth paying: a like button, a view counter, chat message ordering in a non-critical UI, semantic cache lookups, most agent scratchpad or intermediate reasoning state that only that same agent ever reads back.
Close: the more honest design-review question
CAP makes for a good whiteboard moment because partitions are dramatic. PACELC is less exciting and more expensive, because it's the question that's live right now, in production, on every request you're serving. Next time someone in a design review says 'let's make this strongly consistent, just to be safe,' don't ask what happens if the network partitions. Ask what you're already trading away today, on the happy path, to buy that safety. Bring PACELC into that room instead of CAP, and you'll catch the config quietly taxing your p99 for a partition that has never once happened. Day 5: we go inside the else-branch itself — consistency models (strong, eventual, causal, read-your-writes) and which one your users actually need, versus which one your database defaults to.
Extend your knowledge
- ▹Read Daniel Abadi's original 2010 PACELC blog post, then the follow-up paper "Consistency Tradeoffs in Modern Distributed Database System Design: CAP is Only Part of the Story" (IEEE Computer, 2012). Both are short, and both are the primary sources behind this whole framework.
- ▹Pull up your own database's consistency-level docs — MongoDB read/write concerns, Cassandra consistency levels, DynamoDB strong-vs-eventual reads — and figure out which PACELC quadrant your default config actually sits in.
- ▹Look at any vector store or cache in your AI/agent stack and classify it: does staleness matter to the end user, or only to you, the engineer who's nervous about it?
- ▹Preview for Day 5: read up on consistency models — strong, eventual, causal, and read-your-writes. That's the next layer down, inside the Consistency side of PACELC's else-branch.
Discussion
Chat with Chi Cong (AI) about this article. Your conversation is private to you — you can publish a summary for others when you're done.