Short answers to what readers ask most about this topic.
01What is the CAP theorem in simple terms?
The CAP theorem says a distributed data store cannot guarantee consistency, availability and partition tolerance all at once. When a network partition splits the nodes, the system must either refuse some requests to stay consistent or answer everywhere and risk stale or conflicting data. When the network is healthy, it can offer both.
02Why can't you avoid partition tolerance in CAP?
Real networks drop and delay messages, so partitions will happen whether you plan for them or not. A system that ignores them simply fails in an unplanned way. That is why the practical choice is between consistency and availability during a partition.
03What is the difference between a CP and an AP database?
A CP database keeps data consistent during a partition by refusing or delaying requests on the side that cannot reach a majority. An AP database keeps answering on both sides and reconciles differences afterwards. Most databases are tunable, so the label describes a configuration more than a product.
04What does PACELC add to the CAP theorem?
PACELC adds the normal-operation case. If there is a partition, choose availability or consistency; else, choose latency or consistency. It captures the fact that waiting for replicas to confirm a write costs at least one network round trip even when nothing is broken.
05Is PostgreSQL a CP or AP database?
A single PostgreSQL node is not distributed, so CAP applies only once you add replicas. With streaming replication, synchronous replication is off unless synchronous_standby_names is set, so commits do not wait for replicas by default. Turning it on makes commits wait for standbys, which leans consistent but can block if the standbys are lost.
The CAP theorem says that when a network partition splits a distributed database, it must choose between consistency (reads see the latest write) and availability (every request gets an answer). Partitions cannot be avoided, so databases lean CP or AP. PACELC adds that without a partition, they still trade consistency against latency.
Picture a cashier tablet in a carwash POS that loses its Wi-Fi mid-shift while a customer stands at the counter. The question is suddenly concrete: should the tablet refuse the sale, or accept it and reconcile later? I build ERP and POS systems, and this is the version of CAP I meet most often.
That is the whole theorem in one scene. This post answers the question people actually search, what CAP is and how it affects database choice, using worked arithmetic and the official documentation of PostgreSQL, MongoDB and Redis. Every behaviour claimed below is traced to those docs or to the PACELC and CAP references at the end.
What does the CAP theorem actually say?
CAP describes three properties of a distributed data store, and the precise definitions matter because most confusion comes from loose ones.
Consistency: every read receives the most recent write or an error. This is linearizability, not the C in ACID.
Availability: every request to a working node receives a non-error response, without a guarantee that it contains the latest write.
Partition tolerance: the system keeps operating even when the network drops or delays messages between nodes.
The theorem, conjectured by Eric Brewer and proved by Gilbert and Lynch, states that a system cannot guarantee all three at once. The part people forget is the condition: the limit bites only while a partition is happening.
Is CAP really pick two out of three?
Not in practice. On a real network you cannot opt out of partitions, so partition tolerance is not a menu item. The honest framing is that when a partition occurs, you pick consistency or availability, and when the network is healthy you can have both.
That gives two behaviours. A CP system refuses or delays some requests during the partition to avoid returning stale or conflicting data. An AP system keeps answering on both sides and reconciles the divergence afterwards. Neither is better; they are answers to different questions about what a wrong answer costs.
Ask the business question first: is a refused request worse than a stale answer? For a payment ledger the answer is usually that a refusal is cheaper. For a product catalogue page, a slightly old price beats an error page.
How does a partition play out with real numbers?
Take a cluster of 3 nodes that needs a majority to accept a write. The arithmetic below is all derived, with no measurements involved. It shows why odd cluster sizes are the norm and how the read and write quorum sizes decide between consistent and available reads.
// Majority quorum: the smallest group that two partitions cannot both form.
const quorum = (n: number) => Math.floor(n / 2) + 1;
quorum(3); // 2 -> a 3-node cluster survives 1 node down
quorum(5); // 3 -> a 5-node cluster survives 2 nodes down
quorum(4); // 3 -> 4 nodes still survive only 1 down; the 4th node buys nothing
// Read-your-writes holds when the read set and write set must overlap:
// W + R > N
// N = 3, W = 2, R = 2 -> 4 > 3 overlap guaranteed (consistent reads)
// N = 3, W = 1, R = 1 -> 2 > 3 false, a read can miss the latest write (fast, available)
// A 3-node cluster splits 2 | 1:
// side with 2 nodes: can reach quorum(3) = 2 -> accepts writes
// side with 1 node : cannot reach quorum -> must refuse (CP) or diverge (AP)
The split 2 to 1 is the useful case. The two-node side still holds a majority, so it keeps accepting writes. The one-node side cannot, so a CP design makes it refuse, while an AP design lets it accept writes that will conflict later. That refusal on the minority side is exactly what the C in CAP costs you in availability.
What does PACELC add to CAP?
PACELC, proposed by Daniel Abadi, extends the idea: if there is a partition (P), choose availability or consistency (A or C); else (E), when the system runs normally, choose latency or consistency (L or C). The second half is the part CAP never mentions, and it is the one you live with every day.
The reason is replication. Waiting for a replica to confirm a write costs at least one network round trip. As an illustration only, with an assumed 40 ms round trip, a request that performs 10 sequential synchronous commits waits at least 10 times 40 ms, which is 400 ms that the same request would not pay if it committed locally. So a PC/EC system pays latency for consistency all the time, while a PA/EL system chooses speed in both situations.
How do PostgreSQL, MongoDB, Redis and Cassandra behave?
The table summarises the documented defaults and the knob that moves each system along the spectrum. A database is rarely one fixed letter, because most of these settings are tunable per cluster or even per transaction.
Database
Default behaviour
Knob toward consistency
When replicas are unreachable
PostgreSQL streaming replication
Synchronous replication is off unless synchronous_standby_names is set, so commits do not wait for replicas
synchronous_standby_names with FIRST or ANY, plus synchronous_commit
With sync on, commits wait and may never complete if the required standbys are gone
MongoDB replica set
Implicit default write concern is majority, except in some arbiter setups where it is w:1
w: majority, optionally with wtimeout
A majority acknowledgement cannot be gathered by a minority side, so the write is not acknowledged
Redis master with replicas
Asynchronous replication, low latency, a window for data loss
WAIT and min-replicas-to-write narrow the window but do not give strong consistency
With min-replicas-to-write set, the master rejects writes when too few replicas are in range
Cassandra
Default versions are classed PA/EL: available in a partition, low latency otherwise
A higher consistency level per request, trading latency for fresher reads
Stays available and may serve stale data until replicas converge
Read the table as a map of defaults, not a verdict. PostgreSQL out of the box behaves like a single node with an asynchronous copy, MongoDB leans CP by default, Redis leans AP and fast, and Cassandra is classed PA/EL by the PACELC article, so the same word, replication, hides very different promises.
Redis documents that WAIT does not turn a set of instances into a CP system: acknowledged writes can still be lost during a failover. Do not use a Redis replica set as a source of truth for money because WAIT is present.
What do those settings look like in config?
Each of these is a real option from the official docs. The traps are in the comments, because the dangerous behaviour is what happens when the guarantee cannot be met.
# --- PostgreSQL primary (postgresql.conf): lean CP for the ledger ---
# Commits wait until 2 of the 3 named standbys have the WAL record.
synchronous_standby_names = 'ANY 2 (s1, s2, s3)'
synchronous_commit = on
# Trap: if fewer than 2 standbys are streaming, commits block (no timeout).
# Individual low-value transactions can opt out: SET LOCAL synchronous_commit = local;
// --- MongoDB (mongosh): majority acknowledgement, bounded wait ---
db.payments.insertOne(
{ invoiceId: "INV-1042", amount: 85000 },
{ writeConcern: { w: "majority", wtimeout: 5000 } }
);
// If a majority is unreachable, the write is not acknowledged within 5 s.
# --- Redis: asynchronous by default; narrow the data-loss window ---
# redis.conf on the master
min-replicas-to-write 1
min-replicas-max-lag 10
# per write, ask for 1 replica acknowledgement, waiting up to 100 ms
SET session:abc 1
WAIT 1 100
# Trap: WAIT does NOT make Redis a CP system; acknowledged writes can still be lost.
Notice the shape: the CP settings add a wait and a way to get stuck, the AP settings add a window for loss. Choosing is choosing which failure your application is better equipped to handle, a blocked commit or a lost write.
How do I choose a database using CAP and PACELC?
Use CAP as a checklist of questions about the data rather than a label on the database. In the carwash example the sale record and the cached session need different answers, and a single stack can hold both: Postgres for money, Redis for sessions.
List what each dataset costs when it is wrong: a stale read, a lost write, a refused request.
For data where a wrong answer costs money or trust, choose CP behaviour and decide what the user sees when the system refuses.
For data that is cheap to be stale, such as caches and counters, choose AP behaviour and design the reconciliation rule up front.
Run the PACELC question for normal operation: can the request path afford the round trip of a synchronous replica?
Test the failure on purpose, by cutting the replica or the network, and write down what the application actually did.
If you run a single PostgreSQL node on one server, as many small ERP and POS deployments do, there is no replica partition to reason about, and CAP shows up at a different boundary: between the client device and the server. Offline-capable POS clients are an AP decision at that boundary, whether or not anyone named it.
CAP is not a rule that you pick two. It is a reminder that during a partition you choose between refusing and diverging, and PACELC adds that you pay for consistency in latency even on good days. Decide per dataset, read the documented defaults of the database you run, and test the failure before a customer does.