1 Why one database isn't enough
Imagine the whole class shares one notebook to write down everyone's lunch order. At first that's fine. But then the notebook gets full (too much to write), or a hundred kids want to scribble in it at the same time (a huge line forms), or someone spills juice on it and it's ruined forever. So the teacher says: "Let's keep several notebooks, in different rooms." Now nobody waits in one giant line, and if one notebook is ruined we still have the others. But a brand-new problem appears: if I write "pizza" in the Room A notebook, the Room B notebook still says "salad." Which one is correct? That puzzle is what today is all about.
Everything we've built in earlier sessions β the load balancers, the application servers from Session 6 β eventually has to read and write data that outlives a single request: user accounts, messages, orders. That permanent home for data is the storage layer, usually a database. For a while, a single powerful database server handles it all. But a single server hits three hard walls.
The CAP and PACELC theorems tell us what is possible and what is not possible when we talk about distributed systems. A system is distributed the moment it has more than one server, connected via some network. Everything today only matters once you cross that line.
The three walls of a single database
| Wall | What goes wrong | In plain English |
|---|---|---|
| Capacity | The dataset grows bigger than one machine's disk or memory. | The notebook is full. |
| Throughput | Too many reads/writes per second for one machine's CPU and disk. | Too many kids want to write at once. |
| Durability & availability | If that one machine dies (disk failure, power cut), all your data and service are gone. | Juice spilled β notebook ruined, and the whole class is stuck. |
The fix for all three is the same big move: use more than one machine. We call each machine a node. A database spread across many nodes is a distributed database, and the whole collection working together is a cluster. There are two fundamentally different ways to spread data across nodes, and they solve different walls:
- Make copies of the same data on several nodes β called replication. This mainly helps the durability/availability and read throughput walls (Topic 8).
- Split the data into pieces, each piece on a different node β called sharding (or partitioning). This mainly helps the capacity and write throughput walls (Topic 8).
The moment your data lives on more than one node, those nodes must talk over a network β and networks are unreliable. Messages get delayed or lost; a cable gets cut. So the copies can temporarily disagree. Every distributed database is, at its core, a set of carefully chosen answers to one question: "When my nodes disagree, what do I do?"
2 Consistency & the kinds of consistency
If there is only one jar of cookies, you always know exactly how many are left β you put cookies in and take them out of the same jar. But if you keep two jars and try to keep them matching, one jar can fall behind: you ate a cookie from jar A, but jar B still "thinks" it's there. "Consistency" is just the question of whether the jars agree. With one jar, the question doesn't even make sense β there's nothing to disagree with.
When can we even talk about consistency?
Consistency is always about data. A few consequences fall straight out of that:
- Stateless app servers cannot be consistent or inconsistent β they're stateless, they hold no data, so there's nothing to be in or out of sync. Asking "is this app server consistent?" is like asking "at how many km/hr is your door cooking the colour 9?" or the classic IIT ragging question, "what's the colour of an electron?" β the answer is simply undefined. The question doesn't apply.
- Consistency is typically discussed in the context of a database or a cache.
- There must be multiple copies of the same data (replication / caching). If there's only one copy, it's guaranteed up to date β you read and write the same place. The only way data goes stale is having multiple copies (database + cache, or database replicas).
- There must be a read after a write/update. If the data is static/immutable, there's no way to read stale data β it's never updated.
After a contest ends, no new submissions arrive, so the leaderboard is always up to date. We don't say "the leaderboard is consistent" β we say consistency is no longer relevant, because the data has become immutable.
State = some data we wish to preserve across multiple requests. So state == data. A stale read happens when an old value of data is read after an update. Stale reads can (not always) lead to bad user experience. If a stale read is possible, it means we do not have immediate consistency.
The kinds of consistency (a spectrum)
Immediate consistency (aka "Consistent")
- It is impossible for stale reads to happen β every read is guaranteed to read the latest data.
- Only possible when all the copies are always kept in sync, all the time (or you use a quorum).
- Always nice to have, but very hard to achieve β typically requires 2-phase commit, which means high latency and low availability. Because it's so costly, we do not always go with immediate consistency.
Eventual consistency (aka "Not Consistent")
- It is possible for some reads to be stale for some time. But if we wait long enough, eventually we'll be able to read the updated data.
- In 99% of cases, eventual consistency is good enough.
Eventual consistency does not mean "eventually the data will become consistent (latest)." It means every write will eventually reflect in all copies.
- "Eventually the data will be consistent" β WRONG. That would imply a future point in time after which the data always stays consistent. Not true: new writes keep arriving.
- "Eventually every write will reflect in all copies" β CORRECT. Wait long enough and what you wrote earlier shows up everywhere. The data can still be stale, because newer writes are continuously coming in.
Data loss β no consistency whatsoever
- If it's possible for some data to be permanently lost, then there are updates that will never reflect β no matter how long we wait, the data never becomes consistent.
- Very rare. Only relevant for analytics, where the overall trend matters more than any individual data point.
Yes β but they all fall inside the umbrella of eventual consistency. A famous one is read-your-write consistency: if Ashish edits his post, he immediately sees the update, but his friends might not see it right away (they get it eventually). There are still more forms beyond this.
You change your profile photo. The write hits Node A and it says "done." A friend in another region reads from Node B, which hasn't received the update yet, and still sees your old photo β a stale read.
- Under immediate consistency, the system wouldn't have said "done" until Node B (and others) also had the new photo β so your friend always sees the new one. (More waiting for you.)
- Under eventual consistency, your friend sees the old photo for maybe a second, then it catches up. (Faster, but briefly "wrong.")
The short delay before a copy catches up is called replication lag. Usually milliseconds, but it's never exactly zero β light and network packets take time to travel. That's the physical reason "immediate consistency everywhere, instantly" is essentially impossible across distant machines.
3 Availability
Availability is just: "will the shop serve you when you ask?" If a healthy shop with stock turns you away for no good reason, that's bad (low availability). But if you asked for something silly, or the shopkeeper happened to be out for a second and you can just ask again, that doesn't count against the shop.
Availability refers to whether a server can deny a request or not. If a server can deny a request, the system is "not available" / has low availability. If a server always satisfies the request, the system is "available" / has high availability. But the details matter β two classic interview questions:
Think 4xx (bad request) or 3xx (auth) errors. When the user is at fault, the server is allowed to deny the request β it must still respond, but with the appropriate error message. No, this is NOT low availability. We still call the system highly available.
Again, NOT low availability. The request times out, the client just retries, and the load balancer routes the next request to a healthy server.
Low availability is when a healthy request comes to a healthy server, but the server still refuses to provide the service for some internal reason β most often because it can't sync up with other replicas. Examples:
- Internal server error (bad code, or unable to access a critical resource).
- Some specific reason the server wants to deny service.
- The request times out because the server is busy (not because it's down).
At 99.999% availability, a service can have a maximum downtime of about 5.26 minutes per year.
4 Partition tolerance & the bank (A vs P)
Imagine two friends who normally text to keep their notes matching. One day their phones can't reach each other β that's a partition. A system is "partition tolerant" if it keeps working at least partially even while they can't talk, instead of the whole thing shutting down.
What is partition tolerance?
A system is partition tolerant if it can continue functioning seamlessly in the face of network partitions. When a partition happens, if the system continues working at least partially (some availability might be lost, some consistency might be lost, but the entire system doesn't go down), it's partition tolerant. If any loss of communication between nodes brings the whole system down, it's not partition tolerant. Partition tolerance is very highly desired.
What is a network partition?
In a network of servers (a distributed system), if some subset of servers cannot communicate with the other subset, we have a network partition.
The connection doesn't have to physically break for a partition to happen. A slow / congested network behaves like a broken one, because requests time out.
In large-scale distributed systems (Google has 10 million+ servers), network partitions are not just likely, they ALWAYS exist. Some subset is always disconnected β some node or link is always down, creating a small partition somewhere. 100% probability. There is always a partition somewhere.
Availability vs Partition tolerance β the bank/ATM story
Tarun wants to withdraw money from his bank account via the ATM. In the backend, the bank replicates the account information β we don't want to lose account data if a server/hard disk crashes. When Tarun withdraws, the bank must update both copies to reflect the new balance. Now imagine a network partition happens and the two bank database servers can't communicate. Two cases arise:
The bank returns success without updating both servers β it updates the balance in just one server and gives Tarun his money. This gives eventual consistency (stale reads possible, but once the network is back the DB servers sync up) and high availability. The data was inconsistent, but the system stayed available through the partition.
The bank database detects it can't reach its replica and denies service. This is low availability (a healthy request to a healthy server got denied!), but we got immediate consistency β no stale read was possible.
Note: the withdraw money functionality is down, but check balance is still up. So the system as a whole did not crash β it is still partition tolerant. There was low availability for "money withdrawal," but the system didn't crash.
When is a system NOT partition tolerant?
When any network partition brings down the entire system. This is very, very rare β about 99.99% of backend systems are designed to be partition tolerant.
Stock exchanges (NSE / BSE / SSE / NYSE) are A+C systems β they do not have partition tolerance. If two servers inside the exchange can't communicate, the entire exchange shuts down until it's resolved. So they do a lot of engineering to minimise the chance of a partition: each pair of servers is connected by multiple redundant fiber connections.
In reality the ATM system and most banks follow A+P (eventual consistency), because banks have found a way to "earn" from their own mistakes. If, due to a partition, Tarun redeems more money than he has, eventually the partition resolves, both DBs reconcile, and an automatic audit realises he withdrew twice. Tarun's balance is now negative β the bank charges him an overdraft fee plus interest on the βΉ100 he effectively borrowed.
This is also why refunds take 5β7 business days even though payment happens immediately. (Typically the refund actually happens immediately β but in the worst case it can take a long time, because of eventual consistency.)
5 The CAP theorem
You and your sibling each have a copy of the family shopping list, and you keep them the same by texting each other. One day the phone network goes down between you β you literally can't text. Now your sibling walks into the store and asks: "Should I add milk?" You have only two choices. (1) Say "wait!" until the phones work again, so you don't both buy milk β that's safe but you got stuck waiting. Or (2) just decide on your own and sort out any double-buy later β that's you keep going, but your lists might disagree for a bit. When the connection is broken, you simply cannot have both "always agree" AND "never wait." CAP is the grown-up name for that exact dilemma.
CAP / PACELC apply only when you have replication β multiple copies of the same data, distributed across multiple servers, connected by a network. No replication, no theorem.
The CAP theorem (proposed by Eric Brewer in 2000, later proven) is the most famous rule about distributed data stores. It gives you a fundamental limit of what you can achieve with distributed systems: a system can offer at most two of these three guarantees at the same time:
| Letter | Name | Promise it makes |
|---|---|---|
| C | Consistency | Every read sees the most recent write (or an error). All nodes agree on the data β as if there were one single copy. |
| A | Availability | Every request gets a (non-error) response, even if it might not be the very latest data. The system always answers. |
| P | Partition tolerance | The system keeps working even when the network between nodes drops or delays messages (a network partition). |
Google Spanner seems to violate CAP β they claim to provide all three. They don't actually violate it (and don't claim to). They do some black magic with timestamps (TrueTime) to practically achieve all three.
The three categories of system (with real percentages)
Even during a partial failure (network partition), the system keeps serving users' requests. AP systems give up on consistency β they're eventually consistent. 99% of all systems are AP, because mostly we do not care about immediate consistency. Example: social media (Twitter / Facebook / Instagram). If a user keeps seeing an old post, or a new post doesn't appear in a friend's feed immediately, it's not the end of the world β as long as it updates eventually.
Only if there's no partition can the system serve users. The moment a partition happens, it may deny requests (lose availability). Rare β only 0.9%, used when data is critical. Candidate examples: financial transactions & banks (though these are often actually eventually consistent), stock brokers (Zerodha), ticket booking (BookMyShow) / product purchase (don't oversell inventory).
Even IRCTC makes ticket booking eventually consistent β they offer RAC tickets that get resolved later ("eventually"). CP is rare because in most cases we're OK with eventual consistency; immediate consistency is always nice but rarely strictly needed.
Never denies a request and always stays consistent β as long as there's no partition. The moment a partition happens, the entire system crashes. Extremely rare β only 0.1%. Example: stock exchanges (NSE, NYSE, SSE, BSE). A+C systems are typically not geographically distributed because they can't afford partitions: very powerful servers, all in the same building, connected with high-speed fiber, multiple redundant connections. The chance of a partition is close to 0 (by design, extremely expensive). Still, if one happens, the whole system must shut down.
CAP β two ways to state it
1st definition: pick any 2. 2nd definition (the better one): when there's a network partition, you must choose between Availability or Consistency β you cannot have both. As long as there's no partition, you don't have to worry. The moment one happens, you choose C or A, not both.
People say "pick 2 of 3" as if you freely choose any pair. But because in large systems there's always a partition somewhere, you definitely want partition tolerance. So effectively your real choice is between A+P and C+P β "during a Partition, choose Consistency or Availability."
Why you can't have all three during a partition
Picture two nodes, A and B, that normally sync. The network breaks β the partition. A write arrives at A. Node A faces an impossible choice:
- Accept the write (stay Available) β but B doesn't know about it, so a read from B is stale β you gave up Consistency.
- Refuse the write / make it wait until it can reach B (stay Consistent) β but now A isn't answering β you gave up Availability.
There's no third option while the cable is cut. That's the theorem in one breath.
Bank (wants CP). The network between two data centres glitches. You try to withdraw $100. A CP system would rather fail your request ("please try again") than risk letting you and your partner both withdraw the last $100 from two sides of the partition and end up overdrawn. Correctness beats uptime.
Twitter/X-style feed (wants AP). The same glitch happens; you post a tweet. An AP system accepts it immediately. A friend on the other side might not see it for a few seconds. That's fine β a slightly late tweet beats a site that says "down."
By how they behave during a partition: CP examples include MongoDB (default config), HBase, Zookeeper, etcd, traditional RDBMS clusters; AP examples include Apache Cassandra, Amazon DynamoDB, Riak, CouchDB. The textbook "CA" category really just describes a single non-distributed node β once you have multiple nodes over a network, partitions are inevitable, so the meaningful choice is CP vs AP.
CAP isn't a menu where you tick two boxes for fun. Partitions are a fact of nature, so the engineering decision is: when the network splits, would my product rather be correct (CP) or online (AP)? The answer comes from the business, not from the database.
6 The PACELC theorem
CAP only talks about what happens when the phones are broken. But here's the thing: most days, the phones work just fine β and you still have a choice to make! Every time your sibling wants to add something to the list, do you (1) wait to text everyone and make sure all the copies match before saying "ok" β safe but slow? Or (2) just write it down right away and text the others a moment later β fast but the lists briefly differ? PACELC is the smarter rule that says: "Tell me your choice when the phones are broken, AND ALSO your choice on a normal day when they work."
CAP has a blind spot: it only describes behaviour during a partition β a rare event. But a distributed database makes a consistency-vs-speed trade-off on every single request, all the time, even when the network is perfectly healthy. The PACELC theorem (Daniel Abadi, 2010) is an extension to CAP that fills that gap. Read it as a sentence:
When there's a Partition, choose between Availability (serve potentially stale data) OR Consistency (service down, but data stays consistent). Else (even with no partition) there's a trade-off between Latency (low latency because sync-up happens later) OR Consistency (sync up right away, but higher latency).
In symbols: if (P) then (A or C) else (L or C).
Just like in DSA we trade Time Complexity vs Space Complexity, in HLD we trade Consistency vs Availability and Consistency vs Latency. Basically: if you choose immediate consistency, you give up on both Availability and Latency. An always-consistent system will be slow (high latency) and might deny requests (low availability). So immediate consistency comes at a huge cost.
The first half (P β A/C) is just CAP. The new, important half is the "ELC": latency (how long a request takes) is traded against consistency even when everything is healthy. To be strongly consistent, a node must wait to confirm the other copies agree before answering β and waiting is latency.
CAP describes the exceptional moment (a partition). PACELC also describes the normal 99.9% of the time. Since latency directly drives user experience and cost every day, the "Else" branch often matters more in practice than the rare partition branch. PACELC = CAP + the everyday trade-off CAP forgot.
The four PACELC personalities
Each system gets a two-part label: its partition choice (PA or
PC) and its normal-time choice (EL or
EC).
| Label | During partition | Normal time | Example systems |
|---|---|---|---|
PA/EL | Stay available (allow stale) | Favour low latency (allow stale) | Cassandra, DynamoDB, Riak β "fast and always up; consistency is best-effort." |
PC/EC | Stay consistent (may reject) | Favour consistency (accept higher latency) | HBase, BigTable, VoltDB β "always correct, even if slower or briefly down." |
PC/EL | Stay consistent during partition | But favour latency when healthy | MongoDB (typical config), PNUTS β "strict when it matters most, fast on a normal day." |
PA/EC | Stay available during partition | But favour consistency when healthy | Rarer; some tunable configs. Up during outages, careful when calm. |
Two requests arrive on a perfectly healthy network (no partition β the "Else" branch).
Cassandra (PA/EL). You write a value. By default it can reply "ok" as soon as one (or a quorum of) replica acknowledges, without waiting for all copies. Response is super fast (low Latency), but a read a millisecond later from a not-yet-updated replica might be stale. It chose L over C.
A PC/EC store like HBase. The same write waits until the system can guarantee subsequent reads see it. The reply comes back a little slower, but any later read is correct. It chose C over L.
Notice: no partition was involved. CAP can't even describe this difference β but PACELC's "ELC" half captures it precisely.
Many systems (Cassandra, DynamoDB) let you pick the trade-off per request using a quorum: roughly, "how many of the N copies must reply before I answer." Wait for more copies = more consistent but slower; wait for fewer = faster but riskier. So a single database can behave like different PACELC labels depending on the knob β which is why these theorems are guides, not rigid boxes.
if (P) then (A or C) else (L or C) β an extension of CAP. The
first half restates CAP (partition β A vs C); the second, new half says that even on a
healthy network you trade latency against
consistency on every request. Immediate consistency therefore costs you
both availability and latency (like Time vs Space in DSA). Labels like Cassandra's
PA/EL vs HBase's PC/EC capture both branches.
7 Proof by story β Aditi's Reminder Service
Two people answer phone calls and write reminders in their own diaries. Everything you learned today β scaling, consistency, availability, partitions, CAP, PACELC β shows up naturally as their little business grows. Watch what choices they're forced to make.
Scaling up, then out
- Vertical scaling (scaling up): at first Aditi runs the reminder service alongside her day job; as requests grow, she quits and does it full time.
- Horizontal scaling (scaling out): she can't handle all the calls alone, so she hires Sanjana.
Single diary β Aditi & Sanjana must be in the same room (colocated, not distributed), and they'll fight over the diary β it becomes a bottleneck. Two diaries β they can be far apart (Sanjana works from home) and the diary isn't a bottleneck. So they go with two diaries; a single diary would render the scaling useless.
The angry customer (a consistency issue)
Customer: "Yesterday I called to remind me of my 11am flight. Then I called at 8am to ask my flight time and you said you don't remember. You lost my data and I missed my flight." Why? The write went to Sanjana, but the read came to Aditi, and Aditi's data was not in sync with Sanjana's. A consistency issue.
Protocol 1 β sync eventually (at night)
They copy each other's data each night before closing, so in the morning both have the same copy.
PROTOCOL 1 β Sync eventually (at night)
on WRITE(data):
write data to MY diary only
return SUCCESS # fast: write in one place
every NIGHT (before closing):
copy MY diary -> OTHER diary
copy OTHER diary -> MY diary # both diaries now match
Q: Can there still be stale reads / angry calls? Yes β until the nightly sync, any read before the sync can be stale. But wait long enough and the data syncs (eventual consistency). Data can be stale (low consistency), but a write only touches one place (low latency).
Protocol 2 β sync immediately
Whenever one gets a call, they write to their own diary, put the customer on hold, call the other person, get them to write it too, and only return success when the other confirms. If the other returns an error, roll back (delete from own diary) and return error.
PROTOCOL 2 β Sync immediately (synchronous replication)
on WRITE(data):
write data to MY diary
put customer ON HOLD
call OTHER person, ask them to write data
if OTHER responds SUCCESS:
return SUCCESS
else: # OTHER returned error
delete data from MY diary # ROLLBACK
return ERROR
on READ(query):
answer from MY diary directly # no waiting β both have all data
This gives immediate consistency β data is always in sync because every write goes to both diaries. But latency is higher (the client waits on hold while Aditi calls Sanjana), and throughput suffers β every call is now effectively made twice (to both Aditi and Sanjana), which undercuts the horizontal scaling we did.
Q: Does Protocol 2 apply to reads too? No β only writes. On a read, Aditi and Sanjana each respond independently from their own diary (both have all the data). So no waiting on reads (low latency for reads).
It gives eventual consistency β you can make changes even while offline, and they sync up once your network is restored β by performing conflict resolution later. The powerful (but complex) general technique is Conflict-Free Replicated Data Types (CRDTs). Google Docs doesn't actually use CRDTs; it uses a custom protocol that is slightly weaker than (but similar to) CRDTs.
Vacation time (a server is down β but no partition)
Sanjana goes on vacation. No availability issue β calls just route to Aditi. No network partition β Sanjana is simply on leave. When she returns, she syncs with Aditi's diary before taking calls β no consistency issue. This holds for both protocols: since Sanjana is absent, Aditi keeps writing to her own diary (even in Protocol 2), and Sanjana syncs up on return. So no availability and no consistency issue β because there's no partition; it's just that a server (Sanjana) is down.
The big fight (a real network partition)
Aditi & Sanjana have a big fight and refuse to talk β a network partition.
No worries β both keep taking calls and writing in their own diaries. Eventually the fight resolves (network heals) and they sync. Data stays stale until then, but the service stays available.
Partition β High Availability + Eventual Consistency (AP).
The protocol requires writing to both places, but Aditi isn't talking to Sanjana. So on a write call she must deny. She can still serve read requests from her own diary, but writes are denied until the fight resolves.
Partition β Immediate Consistency + Low Availability (CP). Note only writes are unavailable β reads continue β but overall we say the system has low availability.
8 Replication vs Sharding
Two different ways to handle "too much for one notebook." Replication is like photocopying the same notebook so every room has an identical copy β if one is ruined you still have the data, and lots of kids can read at the same time from different copies. Sharding is like tearing the address book by letter: names AβH in one notebook, IβP in the next, QβZ in the last β now no single notebook is full, and three kids can write at once because they're using different books. Copies = safety and fast reading. Splitting = more room and fast writing. Real systems do both.
Replication means explicitly, by choice, having multiple copies of the same data, stored on different servers. These are the two foundational techniques for scaling the storage layer, and they are not the same thing β confusing them is a classic beginner trap.
Isn't replication bad? (the normalization angle)
In RDBMS we study normalization, which says redundancy (duplicate data) is bad because it leads to anomalies (read / write / delete). Consider an un-normalized table:
| Course | Student |
|---|---|
| HLD | Tarun |
| HLD | Sanjana |
| LLD | Harini |
| DBMS | Sanjana |
- Update anomaly: rename HLD β "High Level Design" but miss a row β the two rows now disagree, effectively creating an extra course.
- Delete anomaly: delete Sanjana and you accidentally delete the course DBMS (she was its only entry).
The normalized design removes these anomalies by splitting into separate tables:
| Course ID | Course Name |
|---|---|
| 1 | HLD |
| 2 | LLD |
| 3 | DBMS |
| Student ID | Student Name |
|---|---|
| 1 | Sanjana |
| 2 | Tarun |
| 3 | Harini |
| Student ID | Course ID |
|---|---|
| 1 | 1 |
| 1 | 3 |
| 2 | 1 |
| 3 | 2 |
Normalization fights accidental redundancy within one DB. Replication is deliberate, useful redundancy across servers β a completely different goal: protecting against data loss.
Why do we need replication? Durability β replication
The "D" in ACID stands for durability: any committed transaction is persisted to disk, so after a power failure and restart it's still there. But ACID durability is only about persistent vs volatile memory β it does NOT protect you from HDD failures. The only way to prevent data loss when a hard disk dies is replication: multiple copies of the same data across multiple disks.
There's no point replicating data within the same server (useless redundancy) β you waste disk space and still lose everything if the server crashes. Always replicate across multiple servers (useful redundancy).
Replication vs Sharding β the core distinction
| Aspect | Replication | Sharding |
|---|---|---|
| What it does | Multiple copies of the same data across servers. | Partition data horizontally across servers. |
| One word | Copying | Splitting |
| Servers hold | Different servers (replicas) hold the same data. | Different servers (shards) hold different data. |
| Each node holds | The whole dataset. | One slice of the dataset. |
| If a node dies | Fine β another copy has the data. | That slice is unavailable (unless that shard is also replicated). |
| Analogy | Photocopying the whole notebook. | Tearing the address book AβH, IβP, QβZ. |
Pros & cons
| Replication | Sharding | |
|---|---|---|
| Pros | Minimises chance of data loss (resilience). Only read throughput improves β you can read from any copy, spreading read load across replicas. | Store more data than one server's capacity (scaling out). Both read & write throughput increase β load is distributed across shards. |
| Cons | Increases data size β capacity decreases (3Γ replication β ~3Γ less effective storage). Worsens write throughput (extra writes, even if done in the background). Must keep replicas in sync (consistency troubles). | Complex. Requires consistent hashing for routing. Risk of fan-out queries (joins/transactions across shards). |
You have more data than one server can handle, and you don't want to lose data. So large systems run sharding and replication at the same time: shard the data for capacity & write throughput, then replicate each shard for durability & read throughput. E.g. 4 shards, each with 3 copies β 12 nodes total.
How replication works (leaderβfollower)
The most common pattern is leaderβfollower (masterβslave / primaryβreplica): one leader accepts all writes and streams changes to several read-only followers. We go deep on this β sync vs async, failover, lag β in Session 8.
How sharding works (the shard key)
The rule that decides which row goes to which shard is the shard key (partition key). Range-based splits by key ranges (AβH, IβP, QβZ) β easy range scans but risks hot spots. Hash-based hashes the key β even load, but range scans become hard.
Say we have 4 shards (nodes 0β3) and route each user by hashing their ID:
# pick which shard a user lives on def shard_for(user_id, num_shards=4): return hash(user_id) % num_shards # 0, 1, 2, or 3 shard_for("alice") # -> 2 (alice's row lives on node 2) shard_for("bob") # -> 0 (bob's row lives on node 0) shard_for("carol") # -> 1
A write for "alice" goes only to node 2; a write for "bob" only to node 0 β they happen at the same time on different machines. That parallelism is how sharding scales writes. The trade-off: "list all users alphabetically" must now visit every shard and merge β a fan-out query.
% num_shards
Plain hash(key) % N looks neat, but if you ever add a shard
(N: 4 β 5), almost every key remaps to a different node β a massive reshuffle.
Real systems use consistent hashing, which moves only a small fraction
of keys when a node is added/removed.
What about caching? Caching is kind-of like replication
In caching, your data lives in 2 places: the database and the cache. So it resembles replication β but it's not pure replication.
- Does NOT save you from data loss β caches are volatile (store data in RAM).
- Improves read throughput (good).
- Worsens write latency & throughput (bad) β every write must also touch the cache.
Replication = copying (same data, many copies β safety + read scale). Sharding = splitting (different data, many pieces β capacity + read & write scale). They're orthogonal β use both. And the moment you replicate, the consistency questions from CAP and PACELC come right back, because now you have copies that can disagree.
β Putting it all together
Today we left the comfort of a single machine and entered the world of distributed storage. Here's the one-paragraph story tying all the topics together:
A single database eventually hits walls of capacity, throughput, and durability, so we spread data across many nodes. The instant we have multiple copies talking over an unreliable network, those copies can disagree β consistency (always about data) ranges from immediate (no stale reads, but needs 2-phase commit) to eventual (every write reflects eventually β good 99% of the time) to data loss. Meanwhile availability is about a healthy server denying a healthy request (a 4xx or a crash doesn't count), and a network partition (a slow network counts!) always exists somewhere in big systems. The CAP theorem: during a partition you keep only two of C/A/P, so you really choose CP (~0.9%, stay correct β Zerodha, BookMyShow) or AP (~99%, stay online β social media), while A+C (~0.1%, stock exchanges) abandons partition tolerance. PACELC completes it: Else (no partition) you still trade latency vs consistency on every request β immediate consistency costs you both availability and latency. Aditi & Sanjana's diaries make it concrete (Protocol 1 = AP/low-latency, Protocol 2 = CP/high-latency). The build tools are replication (copies β safety + read scale, the only defence against HDD data loss) and sharding (splits β capacity + write scale), usually combined. Replication is exactly where these trade-offs bite, which is why Session 8 zooms into masterβslave replication.
Quick self-check
Why can't a stateless app server be "consistent" or "inconsistent"?
Consistency is always about data, and stateless servers hold no data β there's nothing to be in or out of sync. The question is undefined, like asking the colour of an electron.
"Eventually the data will be consistent" β right or wrong?
Wrong. That implies a future point after which data stays consistent forever. The correct statement is "every write will eventually reflect in all copies" β data can still be stale because new writes keep arriving.
A server returns a 4xx on a bad request. Is that low availability?
No. The user was at fault and the server still responded (with an error). Low availability is when a healthy server denies a healthy request β usually because it can't sync with its replicas.
How likely is a network partition in a system with millions of servers?
100% β there is always some subset disconnected somewhere. That's why partition tolerance is effectively mandatory and the real choice is AP vs CP.
In CAP, why is "partition tolerance" not really optional for a real distributed system?
Because networks genuinely fail (and a slow/congested network behaves like a broken one). You can't choose to never have a partition, so P is effectively mandatory; the real decision is whether, during a partition, you sacrifice C (become CP) or A (become AP).
A bank's core ledger should lean toward CP or AP, and why?
CP for correctness β better to reject/delay than allow a double-withdrawal. In practice many real banks actually run A+P and reconcile later via auditing (overdraft fee + interest), which is also why refunds can take 5β7 business days.
What does PACELC add that CAP misses?
The "ELC" half: even with no partition (normal operation), the system still trades latency against consistency on every request. Immediate consistency costs both availability and latency.
In Aditi's story, which protocol gives AP and which gives CP during a partition?
Protocol 1 (sync at night) β AP: both keep serving, data is eventually consistent. Protocol 2 (sync immediately) β CP: writes are denied during the fight (low availability) but data stays immediately consistent.
Cassandra is often labelled PA/EL. Decode that label.
During a Partition it favours Availability (stays up, allows stale data); Else, on a healthy network, it favours low Latency (answers fast, again allowing some staleness).
You're running out of disk space because the dataset is too big for one machine. Replication or sharding?
Sharding. Replication copies the whole dataset onto each node, so it doesn't help capacity. Sharding splits the data so 10 nodes hold ~10Γ the data.
ACID durability protects you from a hard-disk failure. True or false?
False. Durability only guarantees committed data survives in persistent (vs volatile) memory across restarts. The only protection against HDD failure is replication across multiple servers/disks.
Why doesn't a single leaderβfollower replication setup scale write throughput?
All writes funnel through the one leader; followers are read-only. To scale writes you need sharding, so different nodes accept writes for different slices in parallel.
π References & Further Reading
Class material
- π Original class notes / handout (Google Doc) β open the shared class material for this session.
- Class handout: "[SST-2028] CAP-PACELC".
Papers, docs & deep dives
- Eric Brewer β CAP Twelve Years Later β the author of the CAP theorem revisits and clarifies what it actually claims.
- Daniel Abadi β PACELC β extends CAP to reason about latency vs consistency when there is no partition.
- Designing Data-Intensive Applications (Kleppmann) β chapters on replication and consistency models that ground both theorems.