Why can likes disagree, but my bank balance can’t?
Riya posts a reel. On her phone it shows 1,024 likes; at the same moment her cousin Meera in Delhi sees 1,021 on the same reel. Neither of them cares. Five minutes later Riya pays ₹500 by UPI, and the app says “Payment pending, please don’t retry” for ten long seconds.
Sir, why can Instagram show two different numbers for my likes, but my bank won’t even let me pay until it is sure?
Because both are distributed systems, with copies of data in many places, and both face the same unavoidable choice. When the network between copies breaks, a system can either stay available and risk different answers, or stay consistent and refuse some requests.
That’s the CAP theorem! Consistency, Availability, Partition tolerance: pick two?
That’s the popular slogan, Kabir, and it’s slightly misleading. Networks will break sometimes, so partition tolerance isn’t optional. The real choice is: when a partition happens, do you give up consistency or availability? Likes choose availability. Money chooses consistency.
And when the network is fine?
Then there is a quieter trade-off between speed and consistency, called PACELC. Today you’ll see both, merge a shopping cart after a split, and learn the ladder of consistency models from “eventual” to “strong”.
The CAP theorem and consistency models
The CAP theorem (conjectured by Eric Brewer in 2000 and proved by Seth Gilbert and Nancy Lynch in 2002) states that a distributed data store cannot simultaneously guarantee Consistency (every read receives the most recent write, i.e. linearizability), Availability (every request to a non-failing node receives a response) and Partition tolerance (the system continues to operate despite lost messages between nodes). Since network partitions cannot be ruled out, a system must choose, during a partition, between consistency (CP) and availability (AP). Consistency models describe the guarantees a system gives about the order and visibility of reads and writes, from strong (linearizable) to eventual consistency.
When copies of data are on different machines and the network between them breaks, a system has two choices: keep answering (and possibly give different answers on each side), or refuse some requests until it can be sure every answer is correct. It can’t do both. Banks refuse; social media keeps answering.
Words you’ll meet in this lesson
Where common systems sit (default settings)
| CP-leaning | ZooKeeper, etcd, HBase, Google Spanner, MongoDB (majority read/write concern), relational databases with synchronous replicas |
|---|---|
| AP-leaning | Cassandra and ScyllaDB (consistency level ONE), Amazon Dynamo-style stores, Riak, CouchDB, DNS, CDN caches |
| Tunable per request | Cassandra (ONE, QUORUM, ALL), DynamoDB (eventually or strongly consistent reads), MongoDB (read and write concerns) |
CAP and consistency at three levels
Beginner Two bank branches and a broken phone line
Imagine one bank account managed by two branches that phone each other after every transaction. One day the phone line breaks.
- Choose consistency: both branches refuse withdrawals until the line is fixed. No mistakes, but customers are turned away (not available).
- Choose availability: both branches keep serving customers. Nobody is turned away, but the two branches may now disagree about the balance (not consistent), and someone could withdraw the same money twice.
Banks choose consistency for balances. Social apps choose availability for likes and views. Both are correct for their data.
Intermediate CP and AP systems, and common myths
- C (consistency) in CAP means linearizability: every read sees the latest successful write, as if there were one copy. A (availability) means every request to a working node gets a non-error response. P (partition tolerance) means the system keeps operating when messages between nodes are lost.
- CP examples: ZooKeeper, etcd, HBase, MongoDB with majority reads and writes, most relational databases with synchronous replication. In a partition, the minority side refuses requests.
- AP examples: Cassandra and DynamoDB-style stores at low consistency levels, DNS, many caches. In a partition, every side answers, and copies reconcile later (eventual consistency).
- Myth 1: “pick any two”. A distributed system can’t give up P, so “CA” really means “not distributed” (or “stops working during a partition”).
- Myth 2: the choice is fixed for a whole system. Many databases let you choose per query (Cassandra consistency levels, DynamoDB strongly consistent reads).
Advanced PACELC, the consistency ladder and conflict resolution
- PACELC (Daniel Abadi): if there is a Partition, choose A or C; Else, choose Latency or Consistency. Strong consistency needs coordination even on good days, which costs round trips, especially across regions.
- Consistency ladder (strongest to weakest): linearizable (real-time order, one copy illusion) → sequential (one agreed order) → causal (cause before effect, e.g. a reply never appears before its comment) → session guarantees (read-your-writes, monotonic reads) → eventual (copies converge if writes stop).
- Conflict resolution in AP systems: last-write-wins (simple, but silently drops concurrent updates and depends on clocks), version vectors (detect concurrent versions and keep both), and CRDTs (data types like counters and sets designed to merge automatically and correctly).
- Global strong consistency: Google Spanner uses tightly synchronised clocks (TrueTime) and consensus to offer linearizable transactions across regions, at the cost of commit latency.
- Design rule: decide consistency per piece of data: balances, inventory counts and seat bookings need strong guarantees; likes, views, recommendations and analytics can be eventual.
Exam tip: State the CAP theorem with all three properties defined, explain why P is unavoidable so the real choice is C or A during a partition, and give one CP and one AP example with a reason (bank balance vs social media likes). Mention eventual consistency and, for extra marks, PACELC.
CP, AP and the consistency ladder
CP: consistent under partition
The side without a majority stops serving. Answers are always correct, but some users see errors. Payments, inventory, leader election, config stores.
Correct or nothingAP: available under partition
Every side keeps serving and copies reconcile later. Users always get an answer, but it may be stale or conflict. Feeds, likes, carts, DNS.
Answer alwaysTunable
The client chooses per request: fast, eventually consistent reads for a feed; strongly consistent reads for checkout.
Per queryThe consistency ladder
| Model | Guarantee | Example where it matters | Cost |
|---|---|---|---|
| Linearizable (strong) | Reads see the latest completed write, in real-time order | Bank balance, seat booking, unique usernames | Highest (coordination on every operation) |
| Sequential | Everyone sees operations in one agreed order | Replicated logs | High |
| Causal | Causes are seen before effects | Comment threads, chat replies | Medium |
| Read-your-writes / monotonic | You see your own changes; reads never go backwards | Profile edits, order status | Low |
| Eventual | Copies converge when writes stop | Like counts, view counts, recommendations | Lowest |
Don’t ask “is our system CP or AP?”; ask “for this data, what is worse during a network fault: showing an error or showing a possibly wrong answer?” The answer is usually different for balances, carts and like counts in the same app.
A partition, handled two ways
Three copies, then the network splits
A network fault separates A from B and C. Every node is still running; they just can’t talk. This is a partition, and in a big cluster it is a matter of when, not if.replicas = {"A": 100, "B": 100, "C": 100} groups = [{"A"}, {"B", "C"}] # the network splits majority = len(replicas) // 2 + 1 for g in groups: print(sorted(g), "has majority" if len(g) >= majority else "minority")['A'] minority ['B', 'C'] has majorityCP: the minority refuses
Only the side with a majority may accept writes (and serve strongly consistent reads). Clients talking to A get errors, but no one ever sees two different balances.def cp_write(group, value, replicas, majority=2): if len(group) < majority: return "error: try again later" for n in group: replicas[n] = value return "ok" r = {"A": 100, "B": 100, "C": 100} print("via A:", cp_write({"A"}, 40, r), "| via B:", cp_write({"B", "C"}, 70, r), "|", r)via A: error: try again later | via B: ok | {'A': 100, 'B': 70, 'C': 70}AP: both sides answer
Every request succeeds, but the two sides now disagree. When the network heals, the system must reconcile the two values, and some updates may need special merging.r = {"A": 100, "B": 100, "C": 100} r["A"] = 40 # AP: A accepts a write on its side r["B"] = r["C"] = 70 # B accepts a different write on the other side print("A answers", r["A"], "| B answers", r["B"], "-> both available, inconsistent")A answers 40 | B answers 70 -> both available, inconsistentQuorums: consistency you can tune
In leaderless stores like Cassandra, choosing W and R per query moves you along the scale: W = R = 1 is fast and AP-like; R + W > N gives strongly consistent reads while still tolerating one failed node.N, W, R = 3, 2, 2 print("R + W > N:", R + W > N, "-> every read overlaps the latest write") print("survives 1 node down for reads and writes:", N - W >= 1 and N - R >= 1)R + W > N: True -> every read overlaps the latest write survives 1 node down for reads and writes: TrueWhen the network is fine: latency vs consistency
Even without failures, a strongly consistent write must wait for a majority to confirm. If replicas are in Mumbai, Singapore and Frankfurt, that wait is one long network round trip.
This is the “ELC” in PACELC: else, choose latency or consistency. Many systems keep strongly consistent data in one region and replicate asynchronously elsewhere.Decide per piece of data
In one food-delivery app: wallet balance → strong; restaurant stock of a limited dish → strong; cart → eventual with merge; ratings and view counts → eventual.
Good designs mix models. The skill is matching each kind of data to the weakest guarantee that is still correct for the business.
Likes and a payment: what Riya sees, and what the copies do
Step through the six frames. Left: Riya’s reel likes and a UPI payment. Right: copies in three regions, eventually consistent for likes and strongly consistent for money.
- Riya likes her own reel; the count jumps to 1,024 instantly.
- Meera in Delhi sees 1,021 on the same reel at the same moment.
- A little later everyone sees the same count, 1,025.
- Riya pays ₹500 to the kirana store by UPI.
- The app shows “payment pending, please don’t retry” for a few seconds.
- The payment succeeds and her balance is exactly right.
- Likes use an AP design: the write is accepted by the nearest copy and replicated to others in the background.
- Delhi’s copy hasn’t received the latest likes yet, so it shows a slightly older number. Nobody is harmed by this.
- Once updates stop for a moment, all copies converge on the same value: eventual consistency.
- Balances use a CP design: a debit counts only when a majority of copies agree, so there is one true balance.
- A network fault cuts Mumbai off. Rather than risk confirming a debit the others don’t know about, the minority side refuses and the app shows “pending”: less available, never wrong.
- Once the majority confirms, the debit is applied exactly once, and every copy shows ₹1,850. (Regions and timings are illustrative.)
Real-time scenarios: choosing consistency
Three live scenarios: the same network split handled by a CP and an AP design (real numbers), a shopping cart merged after a split, and an advanced look at the latency cost of consistency across regions.
Real-time scenario 1One network split, two designs
A three-node cluster loses its network link to node A. Watch a CP bank balance and an AP like counter react to exactly the same failure. (These are the numbers from the program in “Run it on your PC”.)
- The network splits node A from B and C. All three are still running.
- CP balance: A is alone, not a majority, so it refuses requests. B and C, a majority, accept a debit to ₹70. Some users see errors, but nobody ever sees two different balances.
- AP like counter: both sides keep accepting likes: 3 arrive at A and 5 at B. Every user gets an answer, but the two sides disagree.
- When the network heals, the copies must be merged. A naive last-write-wins rule keeps B's 1,005 and silently drops A's 3 likes.
- A CRDT counter stores each node’s increments separately and adds them: 1,008, with nothing lost. AP systems need merge rules designed for the data.
Merging a shopping cart after a split
Shopping carts are a classic AP example: it is better to always accept “add to cart” than to show an error during a sale. Watch what merging two diverged carts really involves.
- Riya’s cart is stored on two replicas. Adding to the cart must always work, so the cart is designed to be available.
- During a partition her phone and her laptop reach different replicas. She adds a lassi on one and a gulab jamun on the other. Both writes succeed.
- When the network heals, the simplest merge is the union of both carts, which keeps every item she added.
- But if she had removed the lassi on one side, a plain union would bring it back. Amazon’s Dynamo paper reported this “deleted item reappears” effect.
- The fix is a smarter data type: an OR-Set CRDT tags each add and remove, so a remove cancels exactly the adds it had seen. The merged cart is correct without any coordination.
PACELC: consistency costs time even on good days
CAP talks only about partitions. But most of the time the network is fine, and there is still a trade-off: every strongly consistent write must wait for other replicas. Watch the latency of one write with replicas in three regions.
- Replicas are in Mumbai, Singapore and Frankfurt. There is no failure. Network round trips are roughly 60 ms to Singapore and 120 ms to Frankfurt.
- Asynchronous: the write is confirmed after the local copy is saved, about 2 ms. Other regions catch up later.
- Majority: a strongly consistent write needs two of three replicas, so Mumbai waits for Singapore: about 60 ms on every write.
- All replicas: waiting for Frankfurt too pushes it to about 120 ms, and any single region being down blocks writes.
- So, per PACELC: else (no partition) choose latency or consistency. Pay the latency only for data like wallets; keep likes and carts fast. (Latencies are illustrative.)
Consistency choices in real products
1. Payments and wallets: strong consistency
UPI and wallet systems treat balances as strongly consistent: a debit is confirmed only once, and during uncertainty the payment stays “pending” and is later settled or reversed rather than guessed.
2. Social counters and feeds: eventual
Likes, views and follower counts are counted on many servers and merged in the background. Showing 1,021 instead of 1,024 for a second is a fine trade for never failing a tap.
3. Ticket and seat booking: strong for the seat
Train, flight and cinema booking must never sell one seat twice, so the final seat assignment is strongly consistent, even though seat-availability views shown while browsing may be slightly stale.
4. Cluster coordination: CP stores
Kubernetes stores cluster state in etcd and Kafka uses KRaft (formerly ZooKeeper): small CP stores based on consensus, where being correct about “who is leader” matters more than answering during a split.
A network partition, handled CP and AP
Three replicas are split into {A} and {B, C}. In CP mode only the majority side accepts writes; in AP mode both sides accept and the values diverge. Run it, then try the challenges: quorum checks, read-your-writes, last-write-wins vs a CRDT counter, a cart merge and a PACELC latency estimate.
replicas = ["A", "B", "C"]
sides = [{"A"}, {"B", "C"}]
majority = len(replicas) // 2 + 1
def side_of(node):
return next(s for s in sides if node in s)
def write(mode, node, value, store):
side = side_of(node)
if mode == "CP" and len(side) < majority:
return "refused (minority side)"
for n in side:
store[n] = value
return "accepted"
for mode in ["CP", "AP"]:
store = {n: 100 for n in replicas}
r1 = write(mode, "A", 40, store)
r2 = write(mode, "B", 70, store)
print(f"{mode}: via A {r1:24} via B {r2:9} -> {store}")majority = len(replicas) // 2 + 1With 3 replicas a majority is 2. Only a side with at least 2 nodes may make decisions in CP mode.if mode == "CP" and len(side) < majority:CP gives up availability on the minority side: it refuses rather than risk a conflicting value.for n in side:In AP mode each side updates its own copies, so after both writes the copies disagree.
Run it on your PC: CP, AP and merging after a split
This program splits three replicas into {A} and {B, C}. In CP mode it refuses writes on the minority side; in AP mode it lets both sides count likes, then merges them two ways: last-write-wins, which loses likes, and a CRDT counter, which keeps them all. Save it as cap_sim.py and run it.
CAP choices and conflict resolution
"""Three replicas, then a network partition splits them {A} | {B, C}.
CP mode: only the side with a majority accepts writes.
AP mode: both sides accept writes, then merge when the network heals.
We merge a like-counter two ways: last-write-wins (loses likes) and a CRDT counter (keeps all)."""
REPLICAS = ["A", "B", "C"]
partition = [{"A"}, {"B", "C"}]
def side_of(node):
return next(s for s in partition if node in s)
def cp_write(node, value, store):
side = side_of(node)
if len(side) > len(REPLICAS) // 2:
for n in side:
store[n] = value
return "accepted"
return "REFUSED (no majority on this side)"
print("CP mode (choose consistency):")
store = {n: 100 for n in REPLICAS} # balance Rs 100 everywhere
print(" write via A:", cp_write("A", 40, store))
print(" write via B:", cp_write("B", 70, store))
print(" majority side (B, C) now holds:", store["B"], "| A must refuse reads and writes until the network heals")
print("\nAP mode (choose availability): a like counter")
likes = {"A": 1000, "B": 1000, "C": 1000}
# during the partition: 3 likes arrive at A, 5 likes at B
likes["A"] += 3
likes["B"] += 5
likes["C"] = likes["B"] # C is on B's side and syncs with it
print(" during partition:", likes, "-> different answers, but every request succeeded")
# merge 1: last-write-wins (keep the value written last, say B's)
lww = likes["B"]
print(" last-write-wins after healing:", lww, "(A's 3 likes are lost)")
# merge 2: a G-counter CRDT keeps a separate count per replica and adds them
increments = {"A": 3, "B": 5}
crdt = 1000 + sum(increments.values())
print(" CRDT counter after healing :", crdt, "(every like kept)")
# In a terminal: python cap_sim.pyRecorded from a real run with Python 3.13; the simulation is deterministic. These are the numbers used in scenario 1. Try moving node C to A's side of the partition: which side can accept writes in CP mode now?
Merging a G-counter, one line at a time
A G-counter (a CRDT) keeps one count per replica. Two replicas counted likes independently; merging takes the maximum for each replica's slot, then the total is the sum. Watch nothing get lost.
Worked examples: real problems, solved and traced in memory
Four consistency problems, from classifying data to merging concurrent updates. Each runs live and is traced.
CP or AP for each kind of data?
Problem statement
Decide CP or AP for each piece of data using one question: during a network fault, is a wrong answer worse than an error?
wallet balance, like count, seat booking, product reviews, leader election
wallet balance -> CP (refuse when unsure) like count -> AP (answer, reconcile later) seat booking -> CP (refuse when unsure) product reviews -> AP (answer, reconcile later) leader election -> CP (refuse when unsure)
Approach
- For each item, record whether a wrong answer is worse than an error.
- Wrong answer worse → CP. Error worse → AP.
- Print the decisions.
Solution: run it live
What happens in memory, step by step
Recorded from a real Python run: press Next to execute one line at a time. Watch variables change in each frame, and lists and dictionaries (numbered #1, #2 …) fill up as objects.
Check consistency levels for a Cassandra cluster
Problem statement
A table has replication factor 3. For each (write level, read level) pair, decide whether reads always see the latest write and how many replica failures each operation tolerates.
levels ONE = 1, QUORUM = 2, ALL = 3
write ONE read ONE : consistent=False, write tolerates 2, read tolerates 2 write QUORUM read QUORUM: consistent=True, write tolerates 1, read tolerates 1 write ALL read ONE : consistent=True, write tolerates 0, read tolerates 2 write ONE read QUORUM: consistent=False, write tolerates 2, read tolerates 1
Approach
- Map level names to numbers of replicas.
- Consistent if R + W > N.
- An operation tolerates N − level failed replicas.
Solution: run it live
What happens in memory, step by step
Recorded from a real Python run: press Next to execute one line at a time. Watch variables change in each frame, and lists and dictionaries (numbered #1, #2 …) fill up as objects.
How last-write-wins loses updates
Problem statement
Two replicas accept updates to the same field during a partition, each with a timestamp. Merge with last-write-wins and list the updates that were silently discarded.
A: address=Baner at 10:05 · B: address=Wakad at 10:03, phone=98xx at 10:04
merged: {'address': 'Baner', 'phone': '98xx'}
lost : [('B', 'address', 'Wakad', '10:03')]Approach
- Collect every update with its key and timestamp.
- For each key keep only the update with the latest timestamp.
- Updates not kept were lost.
Solution: run it live
What happens in memory, step by step
Recorded from a real Python run: press Next to execute one line at a time. Watch variables change in each frame, and lists and dictionaries (numbered #1, #2 …) fill up as objects.
A PN-counter: likes and unlikes that merge correctly
Problem statement
Users can like and unlike. Each replica keeps separate increment (P) and decrement (N) counts per replica. Merge two diverged replicas and compute the true like count.
replica A: +4 −1 · replica B: +6 −2 · both start from the same state
merged: {'P': {'A': 4, 'B': 6}, 'N': {'A': 1, 'B': 2}}
likes: 7Approach
- Keep P and N dictionaries with one slot per replica.
- Merge by taking the maximum of each slot from both copies.
- Value = sum(P) − sum(N).
Solution: run it live
What happens in memory, step by step
Recorded from a real Python run: press Next to execute one line at a time. Watch variables change in each frame, and lists and dictionaries (numbered #1, #2 …) fill up as objects.
Check yourself
Quick revision
Lesson 3.4 in one screen
- CAP: with a network partition, choose consistency (refuse some requests) or availability (answer, maybe stale). P is unavoidable.
- CP examples: ZooKeeper, etcd, HBase, Spanner, majority-configured MongoDB. AP examples: Cassandra/Dynamo at low levels, DNS, caches.
- “Pick any two” is a myth; many databases are tunable per query (R + W > N for strong reads).
- PACELC: else (no partition) choose latency or consistency.
- Ladder: linearizable → sequential → causal → read-your-writes/monotonic → eventual.
- AP conflicts: last-write-wins (loses data), version vectors, CRDTs (counters, OR-Sets merge safely).
- Choose per data: balances, seats and stock strong; likes, views and carts eventual.
Interview and viva questions
State and explain the CAP theorem.
The CAP theorem states that a distributed data store cannot simultaneously provide consistency (every read sees the most recent write), availability (every request to a non-failing node gets a response) and partition tolerance (the system keeps working when network messages between nodes are lost). Because partitions cannot be prevented in a distributed system, the practical meaning is that during a partition the system must sacrifice either consistency (CP: refuse some requests) or availability (AP: answer but possibly return stale or conflicting data).
Give examples of CP and AP systems.
CP: ZooKeeper, etcd and Consul (consensus-based coordination), HBase, Google Spanner, MongoDB with majority read and write concerns, relational databases with synchronous replication. AP: Cassandra and ScyllaDB at low consistency levels, Dynamo-style key-value stores, Riak, CouchDB, DNS and CDN caches. Many systems are tunable, so the classification depends on configuration.
What is eventual consistency?
A consistency model in which, if no new updates are made, all replicas eventually converge to the same value. Reads may temporarily return stale data. It allows high availability and low latency and suits data like likes, views and recommendations, but needs rules for resolving concurrent updates.
What is PACELC?
An extension of CAP by Daniel Abadi: if there is a Partition (P), a system trades Availability (A) against Consistency (C); Else (E), when the network is healthy, it trades Latency (L) against Consistency (C). It highlights that strong consistency has a latency cost even without failures, especially with replicas far apart.
What are CRDTs?
Conflict-free replicated data types are data structures (such as grow-only counters, PN-counters, grow-only sets and observed-remove sets) designed so that replicas can be updated independently and merged in any order, any number of times, always converging to the same correct result without coordination. They make AP designs safe for counters, sets and collaborative editing.
Practice problems
Consistency and quorum exercises. The last two are business tasks.
Majority side
Three replicas split into sides of size 1 and 2. Print which side may accept writes in a CP system.
sides [1, 2]
['right']
Show solution and explanation
sides = {"left": 1, "right": 2}
majority = 3 // 2 + 1
print([s for s, n in sides.items() if n >= majority])How it works: Only a side with a majority of all replicas may decide.
Consistent read?
N = 3, W = 1, R = 2. Print whether reads always see the latest write.
3, 1, 2
False
Show solution and explanation
N, W, R = 3, 1, 2
print(R + W > N)How it works: 1 + 2 = 3 is not greater than 3, so a read could miss the write.
Classify data
Print “strong” for money and seats, “eventual” for everything else.
balance, likes, seat, views
balance -> strong likes -> eventual seat -> strong views -> eventual
Show solution and explanation
for item in ["balance", "likes", "seat", "views"]:
print(item, "->", "strong" if item in ("balance", "seat") else "eventual")How it works: Consistency is chosen per kind of data.
Converged?
Given replica values after updates stop, print whether the system has converged.
{A: 1008, B: 1008, C: 1005}not yet
Show solution and explanation
values = {"A": 1008, "B": 1008, "C": 1005}
print("converged" if len(set(values.values())) == 1 else "not yet")How it works: Eventual consistency promises convergence only after updates stop and replication catches up.
G-counter merge
Merge two G-counters by taking the maximum per slot and print the total.
a {A:5, B:2}, b {A:3, B:7}{'A': 5, 'B': 7} 12Show solution and explanation
a, b = {"A": 5, "B": 2}, {"A": 3, "B": 7}
merged = {k: max(a[k], b[k]) for k in a}
print(merged, sum(merged.values()))How it works: Each slot only grows, so the maximum is always the most recent count for that replica.
Monotonic reads
A user's successive reads return these versions. Print whether any read went backwards.
[12, 14, 13, 15]
went backwards at: [(14, 13)]
Show solution and explanation
versions = [12, 14, 13, 15]
backwards = [(a, b) for a, b in zip(versions, versions[1:]) if b < a]
print("went backwards at:", backwards if backwards else "never")How it works: Monotonic-read consistency forbids this; pinning a user to one replica (or tracking versions) prevents it.
Latency of a quorum write
Replica round-trip times from the coordinator are given. Print the latency of writes with W = 1, 2 and 3 (the time until W replicas have confirmed).
[2, 45, 110] ms
W=1: 2 ms W=2: 45 ms W=3: 110 ms
Show solution and explanation
rtts = sorted([2, 45, 110])
for W in [1, 2, 3]:
print(f"W={W}: {rtts[W - 1]} ms")How it works: The write waits for the W-th fastest replica: stronger guarantees cost latency (PACELC).
Cost of eventual vs strong
A strongly consistent write takes 60 ms, an eventual one 3 ms. A feature makes 2 lakh writes per minute and each request holds a thread for the write time. Print the average busy threads for each (Little's law).
2 lakh/min, 60 ms vs 3 ms
strong : about 200 busy threads eventual: about 10 busy threads
Show solution and explanation
rate_per_s = 2_00_000 / 60
for name, ms in [("strong", 60), ("eventual", 3)]:
print(f"{name:8}: about {rate_per_s * ms / 1000:.0f} busy threads")How it works: Consistency choices change capacity needs too: 20 times more concurrency for the strong version here.
Oversell risk
A flash sale of 500 phones uses an eventually consistent stock counter with 2 seconds of lag at 300 orders per second. Roughly how many phones could be oversold?
500 stock, 2 s lag, 300/s
orders that could see stale stock near the end: 600 so up to ~ 600 phones could be oversold -> use strong consistency at checkout
Show solution and explanation
lag, rate = 2, 300
print("orders that could see stale stock near the end:", lag * rate)
print("so up to ~", lag * rate, "phones could be oversold -> use strong consistency at checkout")How it works: For limited stock, a stale view is fine for browsing, but the final reservation must be strongly consistent.
Real-world scenario questions (asked in company interviews)
Consistency questions from interviews and real designs. Decide your answer first, then open the model answer.
Which parts of a food app need strong consistency?
For a food-delivery app, decide the consistency level for: wallet balance, cart, restaurant ratings, “only 5 left” limited-dish stock, and order status shown to the customer.
The interviewer is checking: can you choose consistency per piece of data with reasons?
Show a strong answer
- Wallet: strong (never double-spend).
- Limited stock: strong at checkout (never oversell); browsing can show a slightly stale count.
- Cart: eventual/available, with a correct merge.
- Ratings: eventual.
- Order status: read-your-writes and monotonic reads per user (status never goes backwards), not necessarily global linearizability.
Avoid: labelling the whole app “CP” or “AP”.
Users sometimes see old profile data
A Cassandra cluster with replication factor 3 uses consistency level ONE for reads and writes. Users occasionally see their old profile after updating it.
The interviewer is checking: do you understand tunable consistency and its trade-offs?
Show a strong answer
With W = 1 and R = 1, R + W = 2 is not greater than N = 3, so a read can hit a replica that hasn’t received the write. Use QUORUM for both (2 + 2 > 3) on profile reads and writes, accepting slightly higher latency, or LOCAL_QUORUM in multi-datacentre setups. Keep ONE for data where staleness is harmless. Make sure clocks are synchronised, since Cassandra resolves conflicts by timestamp (last-write-wins).
Avoid: switching everything to ALL, which fails whenever any replica is down.
Go active-active in two regions?
Leadership wants the app to accept writes in both Mumbai and Hyderabad at the same time, so a regional outage causes no downtime.
The interviewer is checking: do you see the consistency consequences of multi-leader designs?
Show a strong answer
Active-active means concurrent writes to the same data in both regions, so conflicts are certain during network issues. For each entity decide: can it be partitioned by region (each user’s data has a home region), can it merge automatically (counters, sets via CRDTs), or must it be strongly consistent (wallet, inventory), which then needs a single leader or consensus with cross-region latency on every write. Often the answer is active-active for most data plus a single-region leader for money, with fast failover.
Avoid: promising zero downtime and strong consistency everywhere without discussing the latency or conflicts.
“Explain the CAP theorem”
The classic question. Many candidates answer “pick any two of three”.
The interviewer is checking: can you explain it precisely and correct the myth?
Show a strong answer
In a distributed system, when a network partition happens, you must choose between consistency (every read returns the latest write) and availability (every request gets a non-error response). Partitions can’t be prevented, so partition tolerance is required, and “CA” only applies to non-distributed systems. Example: a bank ledger chooses consistency and refuses some requests during a partition; a like counter chooses availability and reconciles later. Without partitions, PACELC adds a latency vs consistency trade-off, and many databases let you tune consistency per query.
Avoid: saying “Cassandra is AP, MongoDB is CP, done” without explaining what is traded and when.