πŸ“š Study Notes / Home / HLD / Session 8
Session 08 Β· Master-Slave Replication

One writer, many readers β€” and how strict we want to be about it

Last session we met the CAP theorem and the idea that data gets copied across machines. Today we zoom right in on the most common copy-pattern in the real world: master–slave replication (one machine you write to, several you read from). We'll see why copies fall behind, what happens when the master dies, and a beautiful little arithmetic trick β€” quorums β€” that lets you dial consistency up or down per query. We assume you've studied none of this; every idea starts with a tiny story before we go deep.

⏱ 34 min readπŸ“– 6 topics

1 Master–slave (leader–follower) replication


Explain like I'm 5

Imagine one teacher with the master notebook. Whenever something new happens, only the teacher is allowed to write it in. But thirty kids each have their own copy of the notebook, and the teacher keeps reading the new lines aloud so every kid can copy them down. If you just want to read something, you ask any nearby kid β€” fast, no queue. But if you want to add something new, you must go to the one teacher. One writer, many readers.

That teacher is the master (modern docs say leader or primary). The kids are the slaves (modern term: followers, replicas, or secondaries). We'll use "master/slave" because that's still the most common interview phrasing, but know the kinder, clearer words too β€” they mean the same thing.

A note on the names

The words "master" and "slave" carry a lot of historical baggage, and many communities deliberately avoid them. You'll see the exact same relationship described as Leader–Follower, Active–Passive, or Primary–Secondary. They are all the same pattern: one node that owns writes, others that mirror it.

The one big idea

In master–slave replication there is exactly one node that accepts writes (the master), and one or more read-only copies (the slaves) that constantly try to mirror it. All changes flow in one direction: master β†’ slaves. This single rule is what makes the whole thing simple to reason about.

Why copy data at all? (recall Session 7)

In Session 7 we said replication buys us three things. Master–slave gives all three:

  • Read scaling β€” the headline benefit today. Most apps read far more than they write (think: 100 page-views per 1 comment posted). Spread reads across many slaves and you serve more traffic.
  • High availability β€” if the master dies, a slave can be promoted to take over (Topic 3).
  • Geography & backups β€” keep a slave in another data centre for disaster recovery, or one nearby your users for low-latency reads.

How a write propagates

When the master accepts a write, it doesn't just change its own data and forget about it. It records the change in an ordered log and ships that log to every slave. The log has different names in different systems but the same job:

SystemName of the replication log
MySQLbinary log (binlog)
PostgreSQLWrite-Ahead Log (WAL)
MongoDBoplog (operations log)

The key word is ordered: every slave applies the changes in the same sequence the master did. Apply them out of order and the copies would disagree. So a slave is really just a machine replaying the master's diary, line by line, forever.

Statement vs row replication

There are two flavours of what the log actually contains. Statement-based ships the SQL command itself (UPDATE accounts SET balance = balance - 10 ...). It's compact but dangerous: a command like NOW() or RAND() would produce different results on each replica! Row-based ships the actual changed rows ("row 42's balance is now 90"), which is bigger but deterministic. Most modern systems prefer row-based for safety.

The topology, drawn out

Your app talks to the cluster through a layer that decides where each query goes. Writes go to the one master; reads fan out to the slaves:

πŸ“±
App
Sends reads & writes
β†’
🚦
Router
Splits read vs write
β†’
✍️
Master
All writes land here
β†’
πŸ“–
Slaves Γ—N
Replay log, serve reads

Read/write splitting β€” how reads actually scale

The trick that scales reads is read/write splitting: the application (or a proxy in front of the database) sends every INSERT/UPDATE/DELETE to the master, and load-balances every SELECT across the pool of slaves.

Worked example β€” splitting in application code

A tiny router that routes by query type. The reads round-robin across three replicas; writes always hit the master:

# db_router.py β€” toy read/write splitter
master   = connect("db-master:5432")
replicas = [connect("db-replica-1:5432"),
            connect("db-replica-2:5432"),
            connect("db-replica-3:5432")]

def run(sql):
    if sql.strip().upper().startswith(("INSERT", "UPDATE", "DELETE")):
        return master.execute(sql)        # writes β†’ the one master
    else:
        replica = random.choice(replicas)  # reads β†’ spread across slaves
        return replica.execute(sql)

Now imagine the master can handle 5,000 reads/sec on its own. Add three equally-powered replicas and your read capacity jumps to roughly 20,000/sec (master + 3 slaves), while writes stay capped at the single master's 5,000/sec. That asymmetry is the whole point: reads scale horizontally, writes do not.

The catch (sets up Topic 2)

The slaves are always slightly behind the master β€” they're replaying a log that arrives a beat late. So a read from a slave might return data that's a few milliseconds (or, on a bad day, seconds) out of date. That gap is called replication lag, and it's the headache we tackle next.

Key takeaway

Master–slave works brilliantly for read-heavy workloads. It does nothing for write-heavy ones β€” there's still only one master taking writes. When writes themselves become the bottleneck, you need sharding (splitting data across many masters), which is the headline topic of Session 9.

Recap One master accepts all writes and records them in an ordered log (binlog/WAL/oplog). One or more slaves replay that log and serve read-only queries. Read/write splitting sends writes to the master and spreads reads across slaves, which scales reads β€” but never writes.

2 Basic setup & the replication factor


Explain like I'm 5

How many copies of the class notebook should exist? If there's only one, and it gets lost, the notes are gone forever β€” useless. So you keep a few copies. And you usually keep an odd number of kids (plus the teacher) in the room, because when they have to vote on something β€” like "who's the new teacher?" β€” an odd number means there's never a tie. There's always a clear winner.

The replication factor, written X, is the number of total copies of the same data:

The formula

X = 1 master + (Xβˆ’1) slaves. So X = 3 means 1 master + 2 slaves; X = 5 means 1 master + 4 slaves; X = 2 means 1 master + 1 slave. Typical values are X = 3 or X = 5.

Replication factor β‰  total servers

X is the number of copies, not the number of machines in your whole database. If the database is sharded, you have shards and replicas. A database can have S = 100 shards, each shard keeping X = 3 copies, for a total of N = 300 servers. (Sharding is Session 9 β€” here we focus on one shard's copies.)

Why an odd number of servers?

Typically we use an odd number of total servers (the master plus an even number of slaves). This is not mandatory β€” you can have an even number β€” but odd gives you a clear majority, which makes tie-breaking trivial when the cluster has to vote (during master election, or during a quorum read/write). With an even number, a split vote can deadlock.

How many copies do you actually need?

  • At least X = 2 (1 master + 1 slave). With only 1 copy there's no replication at all, so any disk failure means data loss.
  • X = 3 is the typical default (1 master + 2 slaves).
  • X = 5 for critical data where you cannot tolerate any data loss. Going above 5 buys almost nothing β€” it's already astronomically unlikely that 5 servers in the same shard fail at once.
  • Very high X (e.g. 100) only when you're using replicas purely to scale read throughput. This is normally done for a cache, not the whole database, and it's usually fine for such a cache to be eventually consistent.
Why not strict consistency with 100 replicas?

If you had 100 read replicas and demanded immediate consistency, your choices are both bad: write-everywhere means every write must touch all ~100 servers (only acceptable if writes are extremely rare), and quorum means every read must touch ~50 servers (which kills your read performance β€” the very thing you added the replicas for). So at huge replica counts you settle for eventual consistency: just write to master + 1 slave.

Common operational questions

QuestionAnswer
What if the master goes down?A re-election happens; one slave becomes the new master.
What if the old master comes back?It rejoins the cluster as a new slave.
What if a slave crashes?Nobody cares β€” slaves die all the time. Reads just go to a different slave.
How does master election happen?Usually via a distributed configuration management system (e.g. Zookeeper); other algorithms exist too.
How do slaves sync with the master?Asynchronously, using the Write-Ahead Log.
Where do writes happen?Always at least at the master (some configs also write to a subset of slaves).
Where do reads happen?In a subset of the slaves.
Recap The replication factor X is the number of copies: X = 1 master + (Xβˆ’1) slaves, typically 3 or 5. It is not the total server count β€” a sharded DB has S shards Γ— X copies. Prefer an odd total so there's always a clear majority for voting. Need β‰₯2 copies to avoid data loss; go to 5 for critical data; go very high only to scale cache reads (eventually consistent).

3 Replication lag & async vs sync replication


Explain like I'm 5

Back to the teacher reading new lines aloud. There's always a tiny delay between the teacher writing a line and a kid in the back row finishing copying it. If you ask that kid right away, he might say "I don't have that yet!" β€” even though the teacher already wrote it. That little delay is replication lag. And the teacher has a choice: wait until everyone finishes copying before moving on (slow but everyone's in sync), or charge ahead and let the kids catch up whenever (fast but some fall behind).

Replication lag is the time gap between a write committing on the master and that same write becoming visible on a given slave. The big design decision is when the master considers a write "done" β€” and that gives us three replication modes.

Asynchronous replication β€” fast, possibly lossy

In asynchronous (async) replication, the master writes locally, immediately tells the client "done!", and ships the change to slaves afterwards, in the background. The client never waits for the slaves.

✍️
1. Master writes
Commits locally
β†’
βœ…
2. Acks client
"Done!" instantly
β†’
πŸ“¨
3. Ships log
To slaves, later
  • Pro: very low write latency β€” the client only waits for the master.
  • Con: if the master crashes after step 2 but before step 3 reaches any slave, that write is gone. The client was told "saved" but it never propagated. This is data loss.

Synchronous replication β€” safe, slow

In synchronous (sync) replication, the master waits for the slave(s) to confirm they've received and stored the change before telling the client "done."

✍️
1. Master writes
Commits locally
β†’
πŸ“¨
2. Ships log
Waits for slave ack
β†’
🀝
3. Slave acks
"Got it & stored"
β†’
βœ…
4. Acks client
Now "done!"
  • Pro: no data loss for an acknowledged write β€” at least one other node has it.
  • Con: the client waits for the slowest slave on every write. And if a synchronous slave goes down or gets slow, writes stall β€” you've coupled the master's availability to the slave's. Pure full-sync (wait for all slaves) is rare for exactly this reason.

Semi-synchronous β€” the practical middle

Semi-synchronous replication is the popular compromise: the master waits for at least one slave to confirm, then acks the client. The rest catch up asynchronously.

This means at least one backup copy always exists (so a single master crash loses nothing), but you don't pay the cost of waiting for every replica. MySQL's semi-sync and PostgreSQL's synchronous_commit with one sync standby both work this way. As you'll see in Topic 4, "wait for at least K of N" is exactly a quorum β€” the same idea generalised.

ModeMaster waits for…Write latencyData loss on master crash?
Asynchronousnothing (acks immediately)LowestPossible β€” unshipped writes lost
Semi-synchronousβ‰₯1 slave to confirmMediumNo (at least one copy exists)
Synchronous (full)all slaves to confirmHighestNo, but writes stall if a slave is down
Worked example β€” how big is the lag, really?

Suppose your master commits a write at t = 0 ms. With async replication on a healthy network:

  • Network hop to slave: ~2 ms
  • Slave applies the log entry: ~3 ms
  • Total lag β‰ˆ 5 ms β€” invisible to humans, almost always fine.

But now the master gets a burst of 50,000 writes/sec and a slave on slow disks can only apply 40,000/sec. It falls behind by 10,000 writes every second. Within a minute the lag is minutes deep β€” a read from that slave shows data from a minute ago. Lag is usually tiny, but under load it can blow up. Always monitor it (e.g. MySQL's Seconds_Behind_Master).

The read-your-own-writes problem

Here's the classic bug async replication causes. A user updates their profile photo (write β†’ master), then the page reloads and reads their profile (read β†’ a slave that hasn't caught up yet). They see their old photo and think the save failed. They saved successfully β€” they just can't see their own write.

Why this is so confusing

The guarantee the user expects is read-your-own-writes consistency (also called read-after-write): "after I write something, I should immediately see it." Async master–slave breaks it because your write and your follow-up read may hit different machines that disagree for a moment.

Common fixes β€” you'll be expected to name at least one:

  • Read from the master for data the user themselves just changed (e.g. read their own profile from the master for the next 30 seconds after an edit).
  • Sticky / monotonic reads β€” pin a given user's reads to one specific replica so they at least don't bounce backward in time between replicas.
  • Wait for the replica to catch up β€” remember the log position of the user's write, and only let a replica serve them once it has applied up to that position.
Connecting back to CAP (Session 7)

Async replication is the everyday face of choosing availability over strict consistency. The slaves are eventually consistent: stop all writes and wait, and every replica will eventually converge to the same data. "Eventually" is doing a lot of work β€” the lag is exactly the window where the system is inconsistent.

Recap Replication lag is how far a slave trails the master. Async = fast but can lose unshipped writes on a crash; sync = no loss but slow and stalls if a slave is down; semi-sync = wait for one slave (the practical middle). Async breaks read-your-own-writes, fixed by reading from the master, sticky reads, or waiting for catch-up.

4 Failover & promotion


Explain like I'm 5

The one teacher with the master notebook goes home sick. Now nobody can add new lines β€” the class is stuck. So the kids quickly pick the kid whose copy is most up-to-date and say "you're the teacher now." From this moment, new lines go to that kid. Picking a new teacher when the old one disappears is failover. But there's a scary failure: what if the old teacher comes back and also thinks she's still in charge? Now there are two teachers and two different notebooks.

Because only the master accepts writes, the master is a single point of failure for writes. If it dies, reads can still be served by slaves, but no new writes can happen until a new master exists. Failover is the process of detecting the dead master and promoting a slave to replace it. Promotion is the act of turning a read-only slave into the new writable master.

The failover sequence

πŸ’”
1. Detect
Master stops responding to heartbeats
β†’
πŸ—³οΈ
2. Elect
Choose the best slave
β†’
πŸ‘‘
3. Promote
Make it writable
β†’
πŸ”€
4. Reconfigure
Point others & clients at new master

1. Detecting the death

The cluster uses heartbeats: nodes ping each other on a timer. If the master misses several heartbeats in a row (say, no reply for 10 seconds), it's presumed dead. Note "presumed" β€” we can't actually tell a crashed master apart from a master that's merely slow or behind a broken network link. That uncertainty is the root of all the danger below.

2. Electing the new master

You don't pick a slave at random. You want the slave that is most caught up β€” the one whose replicated log position is furthest along β€” because any writes it's missing are lost forever once it becomes master. Many systems use a consensus algorithm such as Raft or Paxos to elect a leader safely (we treat these as black boxes here; their whole job is to make sure the cluster agrees on exactly one leader).

3. Promotion

The chosen slave flips from read-only to read-write. The other slaves are told to start replicating from the new master instead of the dead one.

4. Reconfiguration

Clients and the router must learn the new master's address. This is often done with a stable hostname or a virtual IP that gets re-pointed, or via a service-discovery layer, so apps don't need to hard-code which box is master.

Worked example β€” a write lost during failover

Async cluster, master M with two slaves A and B.

  • M commits write #105 and acks the client "saved."
  • M ships #105 to slave A, but crashes before it reaches slave B. So A has #105, B does not.
  • Failover runs. Suppose it accidentally elects B (less caught-up).
  • B becomes master at log position #104. Write #105 is now gone, even though the client was told it succeeded β€” and when A reconnects to B it will be forced to discard #105 to match B.

Lesson: always elect the most up-to-date replica (A), and understand that async failover can still lose the very last unshipped writes. This is the data-loss risk from Topic 2 made concrete.

Split-brain β€” the nightmare

Split-brain happens when a network partition (recall the "P" in CAP from Session 7) makes part of the cluster think the master is dead and promote a new one β€” while the old master is actually alive and still taking writes on the other side of the partition. Now you have two masters, each accepting conflicting writes, with no idea the other exists.

βœ‚οΈ
Partition
Network splits in two
β†’
πŸ‘‘
Old master
Still alive, taking writes
β†’
πŸ‘‘
New master
Promoted, also taking writes
β†’
πŸ’₯
Conflict
Two truths, must merge
Why split-brain is so bad

When the partition heals, both halves have accepted writes the other never saw β€” and they may contradict (both masters sold "the last ticket"). There's no clean automatic answer; you're stuck reconciling conflicting data, sometimes by hand. Prevention beats cure.

How systems prevent split-brain:

  • Quorum / majority rule β€” a node may only act as master if it can talk to a majority of nodes. In a partition, only one side can hold the majority, so the minority side refuses writes. (This is why clusters favour an odd number of nodes β€” 3, 5, 7 β€” so a majority always exists.)
  • Fencing / STONITH ("Shoot The Other Node In The Head") β€” before promoting a new master, forcibly disable the old one (cut its power or network) so it physically cannot keep serving writes.
  • Leases / tokens β€” a master holds a time-limited lease; if it can't renew it (because it's partitioned away), it must stop accepting writes on its own.

A word on multi-master

So far there's exactly one writer. Multi-master (also multi-leader) replication intentionally allows several nodes to accept writes at once β€” handy for writing locally in multiple data centres. The price is that the same row can be edited in two places simultaneously, so you must have a conflict-resolution strategy (last-write-wins by timestamp, version vectors, CRDTs, or app-defined merges). Master–slave sidesteps all of that by having one writer; multi-master trades that simplicity for write availability and locality. Leaderless systems (Dynamo-style) push this even further β€” which leads us straight to quorums.

Recap The master is a single point of failure for writes. Failover detects the dead master (heartbeats), elects the most caught-up slave (often via Raft/Paxos), promotes it, and reconfigures clients. The big danger is split-brain (two masters during a partition), prevented by majority quorums, fencing/STONITH, and leases. Multi-master allows many writers but demands conflict resolution.

5 The four consistency configurations


Explain like I'm 5

You can choose how careful to be when you save a new phone number into your address books. Save it into just one book? Super fast, but you might lose it if that book burns. Save into two books? Safer. Save into every book before you tell anyone "done"? Safest, but you have to wait for the slowest friend to finish writing. Each level of carefulness is a different configuration, and each one trades speed for safety in its own way.

Master–slave is not one fixed thing β€” by choosing how many places a read touches (R) and how many places a write touches (W), you pick a point on the consistency spectrum. Below are the four classic configurations, from least to most consistent. In every one, reads use R = 1 (one random slave, chosen fresh each time via round-robin); what changes is the writes.

Config A β€” No consistency (data loss possible)

R = 1, W = 1. Reads go to any 1 random slave; writes go only to the master.

OperationWhereAvailabilityLatency
Reads (R = 1)Any 1 random slave (round-robin)Very High β€” if a slave dies, just use anotherVery Low β€” reading one place
Writes (W = 1)Only at the masterVery High β€” if master dies, re-electVery Low β€” writing one place

Writes going only to the master doesn't mean slaves have no data β€” they sync up periodically, so the master always has the latest data and the slaves usually lag a bit.

  • Can there be data loss? Yes. If the master's HDD fails with unsynced changes, those changes are lost forever.
  • Can stale reads happen? Yes β€” slaves sync only periodically. But here we don't even care about stale reads, because data loss is worse and this config already allows it.

Config B β€” Eventual consistency

R = 1, W = 2. Use this when eventual consistency is good enough. Reads go to any 1 random slave; writes go to the master + 1 random slave, atomically (random slave chosen round-robin).

OperationWhereAvailabilityLatency
Reads (R = 1)Any 1 random slaveVery HighVery Low
Writes (W = 2)Master + 1 random slave (at least 2 places)Decent β€” re-elect master, or pick another slaveDecent β€” must write 2 places at once (2PC)
  • How do we write to two places at once? Using Two-Phase Commit (2PC): acquire distributed locks so the write to (master + 1 slave) is atomic. If it doesn't succeed in both places, we roll back the partial write and return an error; the client can retry.
  • Can there be data loss? No β€” the data is in at least 2 places. (If both those HDDs fail before any other slave syncs, sure β€” but that's astronomically rare; HDDs fail maybe once every 2–3 years. Want fewer losses? Write to more places.)
  • Can stale reads happen? Yes. The latest write went to Master + Slave A, but a read went to Slave B which hasn't synced yet β†’ stale read.

Config C β€” Immediate consistency: Write-Everywhere

R = 1, W = X. Use this when you need immediate consistency and writes are rare (read-heavy system). Reads go to any 1 random slave; every write goes to all X nodes (master + all slaves) atomically.

OperationWhereAvailabilityLatency
Reads (R = 1)Any 1 random slaveVery HighVery Low
Writes (W = X)All X servers, atomicallyVery Low β€” if even 1 replica fails to ack, the write is denied and others roll backVery High β€” wait for the slowest server, plus any retry/rollback
All X, not "all that are alive"

The protocol says write to all X nodes β€” not "all replicas currently alive." In write-everywhere, if even one replica is down, writes must be denied. Here the master's role is blurred: there's basically no difference between master and slave.

  • Can there be data loss? No β€” every write is replicated everywhere; all copies stay in sync.
  • Can stale reads happen? No β€” all copies are always in sync.

Config D β€” Immediate consistency: Quorum

R = W = (X+1)/2. Use this when you need immediate consistency and writes are frequent (write-heavy or balanced system). The idea: improve writes at the cost of reads. Write-everywhere has lightning reads but glacial writes; trade a little off each and both become decent.

OperationWhereAvailabilityLatency
Reads R = (X+1)/2A majority β€” more than half the replicas (any random copies)Decent β€” at least half must go down to deny a requestDecent
Writes W = (X+1)/2A majority, atomically (master + a random half of slaves)DecentDecent
Majority math

Majority = more than half the total. If replication factor is X, the majority is ceil((X+1)/2). Example: X = 5 β†’ any read or write must succeed at at least 3 nodes to count as successful.

  • Can there be data loss? No β€” we write to a majority, and a majority is at least 2.
  • Can stale reads happen? No β€” you always read the latest data, because a read-majority and a write-majority must overlap in at least 1 server, and that server has the latest write.

Since different replicas can hold different values, every write stamps a timestamp; on read, we return the value with the latest timestamp.

Two common quorum questions

Do we need synchronized clocks across replicas for the timestamp? No β€” the timestamp is generated at the coordinator (the database app server coordinating the write across replicas), so a single clock is used.

What if some servers are down β€” does X change? No. X is a configuration parameter (the minimum copies you must maintain), not the live server count. Quorum always requires success at ceil((X+1)/2) replicas regardless of crashes. Example: X = 7 β†’ majority = 4. If 4 servers crash, only 3 remain, so no reads or writes can happen until enough recover. (It's the DB's job to keep the live count at β‰₯ X most of the time.)

Recap All four configs read from 1 random slave; they differ in writes. No-consistency (W=1, master only) is fastest but loses data. Eventual (W=2, master + 1 slave via 2PC) avoids data loss but allows stale reads. Write-everywhere (W=X) gives immediate consistency with fast reads but brittle, slow writes (1 dead node blocks writes). Quorum (R=W=ceil((X+1)/2)) balances both via a guaranteed majority overlap β€” immediate consistency with decent reads and writes.

6 Tunable consistency & quorums


Explain like I'm 5

You and your friends keep three copies of the same address book. To be sure a phone number is current, you don't have to check all three books β€” you just need a clever rule. If you always write a new number into at least two books, and always read by checking at least two books, then any read is guaranteed to peek into at least one book that has the newest write. Two-plus-two on three books always overlap! That overlap is the whole magic trick.

Instead of the rigid "one master, slaves are read-only" model, many distributed databases let you treat all replicas more equally and tune consistency per query using three numbers:

SymbolNameMeaning
NReplication factorHow many copies of each piece of data exist.
WWrite quorumHow many replicas must confirm a write before it counts as successful.
RRead quorumHow many replicas you query and compare on each read.
The big idea β€” the quorum rule

If W + R > N, then your read set and your write set are guaranteed to overlap in at least one replica β€” so any read is certain to see the latest acknowledged write. This single inequality buys you strong consistency. If W + R ≀ N, the sets can miss each other and you may read stale data (but reads/writes are faster and more available).

Why the overlap is guaranteed (the intuition)

Picture N=3 replicas as three boxes. A write lands in W of them; a read inspects R of them. By the pigeonhole principle, if W + R is bigger than the total N, the two groups can't be completely separate β€” at least one box must be in both. That shared box holds the freshest value, and the reader picks the most recent (each value carries a version/timestamp), so it always sees the latest write.

Worked example β€” N=3, W=2, R=2 (the classic)

Three replicas hold a key, call them R1, R2, R3. We require 2 to confirm writes and read from 2.

  • Write x = 5: it commits on R1 and R2 (W=2 satisfied), so the write is acknowledged. R3 may still hold the old value for a moment.
  • Read x: we query any 2 replicas. The possible pairs are {R1,R2}, {R1,R3}, {R2,R3}. Check each: every single pair contains R1 or R2 β€” the up-to-date ones!
  • So the reader always sees at least one replica with x = 5, compares versions, and returns the newest. Strongly consistent.

Check the rule: W + R = 2 + 2 = 4, and N = 3. Since 4 > 3, overlap is guaranteed. βœ…

Counter-example β€” N=3, W=1, R=1 (fast but stale)

Now we only require 1 replica for each.

  • Write x = 5 commits on R1 only (W=1).
  • Read happens to hit R3, which never got the update β€” it returns the old x = 4. Stale! 😱

Check the rule: W + R = 1 + 1 = 2, and N = 3. Since 2 ≀ 3, no overlap is guaranteed. This is the fastest, most available, most write-friendly setting β€” and only eventually consistent.

Unlocking the full spectrum

Here's the deeper insight from the four configs above: for immediate consistency, R and W don't have to be equal. All that matters is that the read set and write set overlap. Three knobs control everything:

KnobMeaning
X (= N)Replication factor β€” minimum copies maintained for each piece of data at all times.
RNumber of reads β€” minimum servers that must respond with data for a read to succeed.
WNumber of writes β€” minimum servers that must acknowledge for a write to succeed.

The four configurations of Topic 5 are just special points in this space:

SettingEqualsResult
R = 1, W = 1Master-slave, no consistencyWrite at master, read 1 random slave β€” no consistency, perfect availability
R = 1, W = 2Master-slave, eventual consistencyWrite master + 1 slave, read 1 slave β€” eventual consistency, high availability
R = 1, W = XWrite-everywhereImmediate consistency
R = W = (X+1)/2QuorumImmediate consistency, balanced
The consistency rule, restated

R + W ≀ X (and W > 1) β†’ eventual consistency.
R + W > X β†’ the servers written to + servers read from exceeds the total available, so by the pigeonhole principle at least one server is both written to and read from β€” there's an overlap β†’ immediate consistency.

Because we can choose any R and W, we get tremendous power:

  • If R is low β†’ reads are faster and more available (touch fewer servers).
  • If W is low β†’ writes are faster and more available.
  • If R + W > X β†’ the system is immediately consistent.

As R + W increases: availability drops and latency rises (more servers must be alive for requests to succeed), while consistency rises (more likely to find an overlap holding the latest write). So we can not only choose our consistency level β€” we can also tune the system for read-heavy or write-heavy operation. We're no longer stuck picking just "availability" or "consistency"; we can land on any point on the spectrum.

Tuning the dials for different goals

The beauty is you can slide W and R to favour reads, writes, or safety β€” sometimes per query:

Setting (N=3)W + R vs NBehaviourGood for
W=3, R=14 > 3 βœ…Slow writes (all copies), super-fast readsRead-heavy data that rarely changes
W=1, R=34 > 3 βœ…Super-fast writes, slow reads (read all)Write-heavy logging/metrics
W=2, R=24 > 3 βœ…Balanced, still strongly consistentThe sensible default
W=1, R=12 ≀ 3 ❌Fastest & most available, eventually consistentWhen stale reads are tolerable
There's still a trade-off (CAP again, Session 7)

A higher W means more replicas must be up to accept a write β€” so strong consistency reduces write availability during failures. With W=3 on N=3, a single replica being down blocks all writes. Quorums don't escape CAP; they just give you a precise knob to choose your point on the spectrum.

How real systems expose this

This model comes from Amazon's 2007 Dynamo paper and lives on in Apache Cassandra, Amazon DynamoDB, Riak, and others. They're leaderless β€” no single master; you talk to a coordinator node that fans the request out to N replicas and waits for W or R of them.

Worked example β€” Cassandra consistency levels

In Cassandra you set N once (the replication_factor per keyspace) and then choose a consistency level per statement, which sets W or R for that query:

-- Keyspace pins N = 3 copies per row
CREATE KEYSPACE shop WITH replication = {
  'class': 'SimpleStrategy', 'replication_factor': 3 };

-- A write that needs W = 2 (QUORUM of 3 = 2)
INSERT INTO shop.orders (id, total) VALUES (42, 99)
  USING CONSISTENCY QUORUM;

-- A read that also needs R = 2 β†’ W+R = 4 > 3 β†’ strong
SELECT total FROM shop.orders WHERE id = 42
  USING CONSISTENCY QUORUM;

Cassandra's named levels map straight onto W and R: ONE = 1, QUORUM = majority (⌊N/2βŒ‹+1 = 2 when N=3), ALL = N. Use QUORUM for both reads and writes and you've satisfied W + R > N β€” strong consistency, tuned by you, no master required.

Two repair mechanisms (bonus)

What about the replicas that missed the write (like R3 above)? Leaderless systems heal them with read repair (when a read notices a replica is stale, it pushes the fresh value back to it) and anti-entropy / hinted handoff (background processes that catch laggards up). So "eventually consistent" really does converge β€” these are the gears that make "eventually" happen.

How do you write to more than one place atomically?

Whenever a config requires writing to several servers at once (W = 2, quorum, or write-everywhere), those writes must be atomic β€” all-or-nothing. The standard mechanism is Two-Phase Commit (2PC): a prepare phase where every participant locks and votes "ready," followed by a commit phase where the coordinator tells everyone to commit (or, if anyone voted no, to abort and roll back). If it can't succeed everywhere, the partial write is rolled back and the client retries.

2PC β€” videos from the handout
Recap Tune consistency with three numbers: N copies, W replicas to confirm a write, R replicas to read. The rule W + R > N guarantees the read and write sets overlap, giving strong consistency; W + R ≀ N is faster but only eventually consistent. With N=3, W=2/R=2 is the strong-yet-balanced sweet spot. Cassandra/Dynamo expose this via per-query consistency levels (ONE / QUORUM / ALL).

β˜… Putting it all together


Four topics, one storyline: how to keep copies of your data, and how strict to be about them.

In master–slave replication a single master takes all writes and ships an ordered log to read-only slaves, so read/write splitting scales reads (never writes). Because slaves replay that log a beat late, they suffer replication lag; the master chooses to wait for slaves (sync, safe but slow), not wait at all (async, fast but can lose writes and breaks read-your-own-writes), or wait for one (semi-sync, the middle). When the master dies, failover elects and promotes the most caught-up slave β€” while guarding against split-brain with majority quorums and fencing. And if you drop the single master entirely, tunable consistency lets every query pick its own safety level with N, W, and R, where W + R > N is the line between strong and eventual consistency β€” exactly the CAP trade-off from Session 7, now with a precise dial.

Quick self-check

In master–slave replication, which operations can the slaves handle, and which must go to the master?

Slaves serve reads (SELECTs). All writes (INSERT/UPDATE/DELETE) must go to the single master. That's read/write splitting, and it's why reads scale but writes don't.

A user updates their avatar then immediately sees the old one. What's happening and how do you fix it?

Their write went to the master but their follow-up read hit a slave that hadn't caught up yet (replication lag) β€” the read-your-own-writes problem. Fix by reading the user's own recently-changed data from the master, using sticky reads, or waiting until the replica reaches the write's log position.

Which is safer against data loss on a master crash: async or semi-sync? Why?

Semi-sync. It waits for at least one slave to confirm before acking the client, so an acknowledged write always exists on a second node. Pure async acks immediately, so writes not yet shipped to any slave are lost if the master dies.

What is split-brain and what's the simplest way to prevent it?

During a network partition, two nodes both believe they're master and accept conflicting writes. Prevent it by requiring a node to hold a majority quorum to act as master (so only one side of a partition can write) β€” which is why clusters use an odd number of nodes β€” backed by fencing/STONITH.

With N=3, you set W=2 and R=2. Is this strongly consistent? Show the rule.

Yes. W + R = 2 + 2 = 4, which is greater than N = 3, so the write set and read set must overlap in at least one replica β€” and that replica has the latest value. Strong consistency.

You want the fastest, most available writes and can tolerate stale reads. Roughly where do you set W and R (N=3)?

W=1 and R=1. Then W + R = 2 ≀ N = 3, so overlap isn't guaranteed β€” reads may be stale β€” but both reads and writes succeed by touching just one replica, maximising speed and availability. That's an eventually-consistent configuration.

If the replication factor is X = 5, how many servers form the master/slave split, and what's the quorum majority?

X = 5 means 1 master + 4 slaves (5 total copies). The majority is ceil((X+1)/2) = ceil(6/2) = 3, so any quorum read or write must succeed at at least 3 nodes.

In a write-everywhere (W = X) config, one of the slaves is down. Can a write succeed?

No. Write-everywhere requires the write to land on all X nodes, not just the ones alive. If even one replica is down or fails to acknowledge, the write is denied and the others roll back. That's why write-everywhere is reserved for rare-write, read-heavy systems.

Does the replication factor X shrink when a replica crashes? How does quorum handle a crash with X = 7?

No β€” X is a configuration parameter (minimum copies to maintain), not the live server count. With X = 7 the majority is 4. If 4 servers crash, only 3 remain, so neither reads nor writes can succeed (quorum needs 4) until enough replicas recover.

πŸ“š References & Further Reading


Class material

Papers, docs & deep dives