1Show the Level 1 solution
2**A.**
31. **Read-your-writes** failure: the reload hit a lagging replica. Fix: read the user's own content from the leader for a short time after they write, or wait for the replica to reach their write's log position.
42. **Monotonic reads** failure: reads bounced between replicas with different lag. Fix: stick each user to one replica.
53. **Lost writes on async failover.** Fix: synchronous or semi-synchronous replication for orders, so a confirmed order exists on at least two machines.
64. **Split brain.** Fix: majority-based leader election and fencing of the old leader.
7
8**B.**
91. **Payments: synchronous** (or semi-sync to at least one follower). A confirmed payment must survive the leader failing.
102. **Page-view counters: asynchronous.** Losing a few seconds of views is fine; speed matters.
113. **Cross-continent reporting replica: asynchronous.** Waiting for a round trip across continents on every write would slow the whole site, and reports tolerate lag.
Show the Level 2 solution
1. Falling behind at 100 a second: 100 × 600 = **60,000 changes** behind. Catching up at 900 − 500 = 400 a second: 60,000 ÷ 400 = **150 seconds**.
2. About 2 s × 1,000 = **2,000 confirmed writes** could be lost.
3. 5,000 × 0.7 = 3,500 per replica; 20,000 ÷ 3,500 ≈ 5.7 → **6 replicas** (plus one spare is wise).
4. **W = 3, R = 3**: 3 + 3 = 6 > 5, and with 2 nodes down, 3 are still up for both writes and reads.
1Show the Level 3 solution
2Tested solution (Python 3), the key parts:
3
4```python
5class Leader(Node):
6 def __init__(self, name, followers):
7 super().__init__(name)
8 self.log, self.followers = [], followers
9
10 def write(self, key, value):
11 self.log.append((key, value))
12 self.data[key] = value
13 self.applied = len(self.log)
14 return self.applied # the write's log position
15
16 def replicate(self, follower, max_entries): # asynchronous shipping
17 for key, value in self.log[follower.applied:follower.applied + max_entries]:
18 follower.data[key] = value
19 follower.applied += 1
20
21
22def read_your_writes(user_last_lsn, leader, follower, key):
23 if follower.applied >= user_last_lsn:
24 return follower.data.get(key), follower.name
25 return leader.data.get(key), leader.name
26
27
28def quorum_read(replicas, key, r):
29 answers = [rep.store.get(key, (0, None)) for rep in replicas if rep.up][:r]
30 if len(answers) < r:
31 raise RuntimeError("not enough replicas")
32 return max(answers)[1] # highest version wins
33```
34
35All checks pass, including the async failover that loses `order:7`, `order:8` and `order:9`. The full file is in `code/ch14_replication.py`.
1Show the Level 4 solution
2**The bug:** it writes to the **primary** and immediately reads from a **replica**, which hasn't received the change yet.
3
41. The `UPDATE` commits on the primary.
52. Replication to the replica takes some milliseconds (more under load).
63. The `SELECT` runs on the replica before the change arrives, and returns the **old address**.
74. The page shows the old address; the user thinks it failed and changes it again.
8
9**Fix 1:** read back from the **primary** (or simply return the value just written):
10
11```python
12def update_address(user_id, address):
13 primary.execute("UPDATE users SET address = %s WHERE id = %s", (address, user_id))
14 return primary.query_one("SELECT address FROM users WHERE id = %s", (user_id,))
15```
16
17**Fix 2: read-your-writes by log position.** Record the log position of the user's write and send that user's reads to the primary until a replica has replayed past it (the chapter's `read_your_writes`). A simpler version: route a user's reads to the primary for a few seconds after any write by them.
1Show the Level 5 (sketch) solution
21. **Layout:** a leader in Mumbai, one **synchronous** (semi-sync) follower in another Mumbai availability zone (no lost orders on failover), two or more **asynchronous** read replicas in Mumbai for browsing, and one asynchronous replica in Frankfurt for European reads.
32. **Routing:** writes, carts and "my orders" go to the leader (or use read-your-writes by log position); book pages, search and recommendations go to replicas, with each user stuck to one replica; reports use a separate replica.
43. **Failover:** Patroni or a managed service, using a 3-node etcd cluster for majority-based election; promote the synchronous follower (it has every confirmed order); fence the old leader by term number; apps reconnect through a stable endpoint; retries with backoff.
54. **European writes:** go to the Mumbai leader (one leader per record, no conflicts), accepting extra latency on checkout; browsing reads are served from the Frankfurt replica. Multi-leader would cut write latency but bring conflicts, not worth it for orders.
65. **Monitor:** replication lag per replica, synchronous standby health, failover events, read/write split ratios, leader CPU and write rate, and backup success (replicas aren't backups).