PACELC Theorem in System Design: Latency vs Consistency Explained
The PACELC theorem says that a replicated database faces two trade-offs. If there is a network Partition, it must choose between Availability and Consistency; Else, when the network is healthy, it must choose between Latency and Consistency. It extends the CAP theorem to cover normal operation, where systems spend almost all their time.
This tutorial explains each letter in plain terms, shows where the extra latency comes from, simulates both trade-offs in Java and classifies real databases. It also covers points many guides skip: per-request consistency settings and how quorum sizes set your latency.
1. What Is the PACELC Theorem?
A replicated database keeps copies of the same data, called replicas, on several machines, so it survives failures and serves users from nearby. Keeping copies in step takes time and messages; database replication covers the mechanics. PACELC, proposed by Daniel Abadi in 2010 and published in 2012, names the choices this forces:
- P (Partition): the network splits, and some replicas cannot reach others.
- A (Availability): every request still gets an answer.
- C (Consistency): every read sees the latest successful write.
- E (Else): the network is working normally.
- L (Latency): how long a request takes to answer.
A system is described with two halves, such as PA/EL or PC/EC: the first half is its behaviour during a partition, the second its behaviour the rest of the time.
2. Why the CAP Theorem Alone Is Not Enough
The CAP theorem says that during a partition a system cannot be both fully consistent and fully available. That is true, but partitions are rare. Most of the day the network works, and CAP says nothing about that time.
Even on a healthy network, a strongly consistent read must check with other replicas, often in another region, before answering. That costs time on every request, not just during failures. PACELC makes this everyday cost visible, so "we are CP" stops being the whole answer in a design review.
3. How the PACELC Theorem Works
3.1 The Partition Case: A or C
Imagine a food-delivery app with its primary database in Mumbai and a read replica in Singapore. The link between them fails, and a Singapore user asks for the price of paneer tikka. A PA system answers from the Singapore replica, which may hold an old price. A PC system refuses until it can confirm the latest value with Mumbai. Neither is wrong: a menu can show a slightly old price, but a payment must never be charged twice.
3.2 The Else Case: L or C
Now the link is healthy, but Mumbai is about 140 ms away and the local replica 3 ms away. An EL system answers from the local replica, accepting that the latest change may not have arrived. An EC system asks Mumbai, or a majority of replicas, and pays that round trip on every read. Read more about latency and consistency on their own pages.
4. Where the Latency Comes From
Consistency costs latency because a replica cannot know it holds the newest value without asking others. Many databases use quorums: with N copies, a write waits for W acknowledgements and a read for R responses. If R + W > N, every read overlaps at least one copy that saw the latest write.
The catch is that a request is only as fast as the slowest replica it waits for. R = 2 means waiting for the second-fastest response, so raising W or R buys consistency with latency, and a far-away replica sets your tail latency. Example 3 shows the numbers.
5. PACELC in Java: Code Examples
The examples simulate a primary in Mumbai and a replica in Singapore with fixed delays, so the output is the same on every run. They need Java 17 or later because they use records.
5.1 The Replicated Store
package com.javabytechie.pacelc;
import java.util.HashMap;
import java.util.Map;
// A tiny simulation of a primary database and one read replica in the user's region
public class ReplicatedPriceStore {
public enum ReadPreference { NEAREST_REPLICA, PRIMARY } // the "Else" choice: latency or consistency
public enum PartitionPolicy { STAY_AVAILABLE, STAY_CONSISTENT } // the "Partition" choice
public record ReadResult(int price, int latencyMs, String servedBy) { }
static final int REPLICA_LATENCY_MS = 3; // replica in the user's own region
static final int PRIMARY_LATENCY_MS = 140; // primary on another continent
private final Map<String, Integer> primary = new HashMap<>();
private final Map<String, Integer> replica = new HashMap<>();
private final PartitionPolicy partitionPolicy;
private boolean partitioned;
public ReplicatedPriceStore(PartitionPolicy partitionPolicy) {
this.partitionPolicy = partitionPolicy;
}
public void write(String item, int price) {
primary.put(item, price); // the replica is updated later, by replicate()
}
public void replicate() {
if (!partitioned) {
replica.putAll(primary); // asynchronous copy, simulated as one step
}
}
public void setPartitioned(boolean partitioned) {
this.partitioned = partitioned; // true: the user's region cannot reach the primary
}
public ReadResult read(String item, ReadPreference preference) {
if (partitioned) {
if (partitionPolicy == PartitionPolicy.STAY_CONSISTENT) {
throw new IllegalStateException("primary unreachable, refusing to return possibly stale data");
}
return new ReadResult(replica.get(item), REPLICA_LATENCY_MS, "replica");
}
if (preference == ReadPreference.PRIMARY) {
return new ReadResult(primary.get(item), PRIMARY_LATENCY_MS, "primary");
}
return new ReadResult(replica.get(item), REPLICA_LATENCY_MS, "replica");
}
}
5.2 Example 1: The Else Case (EL vs EC)
package com.javabytechie.pacelc;
import com.javabytechie.pacelc.ReplicatedPriceStore.PartitionPolicy;
import com.javabytechie.pacelc.ReplicatedPriceStore.ReadPreference;
public class ElseCaseDemo {
public static void main(String[] args) {
ReplicatedPriceStore store = new ReplicatedPriceStore(PartitionPolicy.STAY_AVAILABLE);
store.write("paneer-tikka", 249);
store.replicate();
store.write("paneer-tikka", 279); // price changes; the replica has not caught up yet
System.out.println("EL read: " + store.read("paneer-tikka", ReadPreference.NEAREST_REPLICA));
System.out.println("EC read: " + store.read("paneer-tikka", ReadPreference.PRIMARY));
store.replicate();
System.out.println("EL read after replication: " + store.read("paneer-tikka", ReadPreference.NEAREST_REPLICA));
}
}
EL read: ReadResult[price=249, latencyMs=3, servedBy=replica]
EC read: ReadResult[price=279, latencyMs=140, servedBy=primary]
EL read after replication: ReadResult[price=279, latencyMs=3, servedBy=replica]
The EL read is about 47 times faster but returns the old price, 249. The EC read returns the new price, 279, but takes 140 ms. Once replication catches up, the fast read is also correct.
5.3 Example 2: The Partition Case (PA vs PC)
package com.javabytechie.pacelc;
import com.javabytechie.pacelc.ReplicatedPriceStore.PartitionPolicy;
import com.javabytechie.pacelc.ReplicatedPriceStore.ReadPreference;
public class PartitionDemo {
public static void main(String[] args) {
for (PartitionPolicy policy : PartitionPolicy.values()) {
ReplicatedPriceStore store = new ReplicatedPriceStore(policy);
store.write("paneer-tikka", 249);
store.replicate();
store.write("paneer-tikka", 279);
store.setPartitioned(true); // the link between the regions fails
store.replicate(); // nothing can be copied now
try {
System.out.println(policy + ": " + store.read("paneer-tikka", ReadPreference.PRIMARY));
} catch (IllegalStateException e) {
System.out.println(policy + ": error, " + e.getMessage());
}
}
}
}
STAY_AVAILABLE: ReadResult[price=249, latencyMs=3, servedBy=replica]
STAY_CONSISTENT: error, primary unreachable, refusing to return possibly stale data
The available store still answers, with the stale price. The consistent store refuses, and the app must turn that into a friendly message or a retry.
5.4 Example 3: Quorum Sizes and Latency
package com.javabytechie.pacelc;
import java.util.Arrays;
public class QuorumLatency {
public static void main(String[] args) {
int[] replicaLatencyMs = {4, 35, 140}; // three copies: same zone, nearby region, far region
Arrays.sort(replicaLatencyMs);
int n = replicaLatencyMs.length;
int[][] settings = {{1, 1}, {2, 2}, {3, 1}}; // {W, R}
System.out.println("W R overlap? write ms read ms");
for (int[] s : settings) {
int w = s[0], r = s[1];
boolean overlap = r + w > n; // every read quorum shares a node with every write quorum
int writeMs = replicaLatencyMs[w - 1]; // wait for the W fastest acknowledgements
int readMs = replicaLatencyMs[r - 1]; // wait for the R fastest responses
System.out.printf("%d %d %-8s %8d %7d%n", w, r, overlap ? "yes" : "no", writeMs, readMs);
}
}
}
W R overlap? write ms read ms
1 1 no 4 4
2 2 yes 35 35
3 1 yes 140 4
W=1, R=1 is fast, but reads may miss the latest write. W=2, R=2 overlaps and costs 35 ms each way. W=3, R=1 gives fast consistent reads, but every write waits 140 ms for the farthest copy and fails if any replica is down.
6. CAP vs PACELC and Real Databases
| CAP theorem | PACELC theorem | |
|---|---|---|
| Question | What happens during a partition? | What happens during a partition, and what happens otherwise? |
| When it applies | Only during network failures | All the time |
| Choices | Consistency or availability | A or C during a partition; L or C otherwise |
| Typical labels | CP, AP | PA/EL, PC/EC, PA/EC, PC/EL |
| System | Default label | Notes |
|---|---|---|
| Apache Cassandra | PA/EL | Consistency level per query: ONE is fast, QUORUM or ALL is stronger |
| Amazon DynamoDB | PA/EL | Reads are eventually consistent by default; ConsistentRead=true gives strongly consistent reads at twice the read cost |
| Azure Cosmos DB | Configurable | Five consistency levels, from Strong to Eventual; the default is Session |
| Google Spanner | PC/EC | Strongly consistent reads and writes; commits wait out clock uncertainty |
| Apache HBase | PC/EC | Each row is served by one server; its rows are unavailable until another server takes over |
| Yahoo PNUTS | PC/EL | The unusual mix: local, possibly stale reads, but no updates to records whose master is unreachable |
These are default behaviours. Per-request settings move many systems between EL and EC, so a single label rarely tells the whole story.
7. Common Mistakes
- Treating labels as fixed: Cassandra's
QUORUMor DynamoDB'sConsistentReadchange the Else choice per request. - Ignoring the E case: "we are CP" says nothing about the latency users pay every day.
- Thinking PA/EL loses data: replicas normally converge, but conflicting writes need a rule such as last-write-wins, which can silently drop one update.
- Claiming "CA": partitions happen in any networked system; CA only describes a single-node database.
- Testing from one region: a 3 ms local test hides the 140 ms EC cost that distant users pay.
- Assuming EC needs every replica: a majority quorum is enough; waiting for all copies also hurts availability, as Example 3 shows.
8. Best Practices
- Decide per operation, not per system: use EC for stock and payments, EL for product pages and feeds.
- Use read-your-writes where full EC is too slow: users then always see their own changes.
- Keep quorum members close: place a majority of replicas in nearby zones so EC reads stay fast.
- Handle PC refusals in the app: use timeouts, retries with backoff and clear error messages.
- Be honest about staleness: tell users when data may take a few seconds to update.
- Measure tail latency from where users are: watch p99, not just the average.
9. Summary
- During a partition, choose availability (PA) or consistency (PC).
- The rest of the time, choose latency (EL) or consistency (EC).
- Consistency costs latency because replicas must talk before answering; quorum sizes set how much.
- Real systems have default labels but often allow per-request choices.
- Pick the trade-off per operation, based on what stale data would cost.
The PACELC theorem turns "CP or AP?" into the more useful question of what each operation should pay in latency for fresh data. Open the FAQ tab for PACELC theorem interview questions.
PACELC Theorem Interview Questions and Answers
Q1. What is the PACELC theorem in simple terms?
The PACELC theorem says that a replicated database must choose between availability and consistency during a network partition, and between latency and consistency the rest of the time. It extends the CAP theorem to cover normal operation.
Q2. What does each letter in PACELC stand for?
P is Partition, A is Availability and C is Consistency, for the failure case. E is Else, meaning normal operation, followed by L for Latency and C for Consistency. A system is described with two halves, such as PA/EL.
Q3. How is PACELC different from the CAP theorem?
CAP only describes the trade-off during a network partition. PACELC adds the trade-off between latency and consistency when there is no partition, which is the situation a system is in almost all the time.
Q4. What does PA/EL mean? Give an example.
PA/EL means the system stays available during a partition and prefers low latency otherwise, accepting that reads may be stale. Cassandra and DynamoDB with their default settings are common examples.
Q5. What does PC/EC mean? Give an example.
PC/EC means the system keeps data consistent in both cases: it refuses some requests during a partition and accepts higher latency otherwise. Google Spanner and Apache HBase are examples.
Q6. Why does strong consistency increase latency even without a partition?
A replica cannot be sure it has the newest value without checking with other replicas or the primary, which may be in another region. That extra round trip is added to every strongly consistent request, even when the network is healthy.
Q7. Can one database be both EL and EC?
Yes. Many databases let you choose per request: Cassandra has consistency levels such as ONE and QUORUM, and DynamoDB has a ConsistentRead flag. The same database can serve fast reads for a product page and consistent reads for checkout.
Q8. What is PC/EL and does any system use it?
PC/EL means the system gives up availability during a partition but prefers low latency otherwise. Yahoo's PNUTS is the classic example: reads come from a local, possibly stale copy, but a record cannot be updated while its master is unreachable.
Q9. How do quorums relate to PACELC?
With N replicas, a write waits for W acknowledgements and a read for R responses. When R + W is greater than N, reads see the latest write, giving EC behaviour, but each request waits for slower replicas, which adds latency.
Q10. Which PACELC choice should an e-commerce checkout use?
Checkout steps such as reserving stock and taking payment should use consistent reads and writes, because stale data there causes overselling or double charges. Product browsing can use fast, eventually consistent reads in the same system.