1Show the Level 1 solution
2**A.**
31. **Queue**: a job for one worker.
42. **Topic**: a fact many services care about.
53. **Topic**: search, cache, recommendations and price alerts all react.
64. **Queue**: one email, sent once.
75. **Topic**: feeds, notifications and recommendations react.
8
9**B.**
101. **False.** Inside a group, each message goes to one member.
112. **True.** The feed is precomputed; reading is one lookup.
123. **False.** With fan-out on read, posting is one write for anyone; it's fan-out on **write** that struggles with celebrities.
134. **False.** A crash after the commit and before the publish loses the event.
145. **True.** The relay can publish, crash before marking, and publish again.
1Show the Level 2 solution
21. 5,000,000 ÷ 50,000 = **100 seconds**.
32. 10,000 × 200 = **2 million** feed writes a day.
43. 1,000,000 × 300 = **300 million** lookups a day (before caching).
54. 1,000 × 4 = **4,000** deliveries a second.
65. Up to **1 second** waiting for the next poll, plus about 20 ms to publish: about **1.02 seconds** worst case. CDC usually gets this lower.
1Show the Level 3 solution
2Tested solution (Python 3), the key parts:
3
4```python
5class Broker:
6 def subscribe(self, topic, group, member):
7 self.subs[topic].setdefault(group, []).append(member)
8
9 def publish(self, topic, msg):
10 for group, members in self.subs[topic].items(): # every group
11 m = members[self.rr[(topic, group)] % len(members)] # one member each
12 self.rr[(topic, group)] += 1
13 self.inbox[m].append(msg)
14
15
16class Hybrid:
17 def post(self, author, book, t):
18 self.posts[author].append((t, author, book))
19 if author in self.celebs:
20 return # celebrities: pull at read time
21 for u in self.followers[author]:
22 self.feeds[u].append((t, author, book))
23
24 def read(self, user, k=10):
25 items = list(self.feeds[user])
26 for a in self.following[user] & self.celebs:
27 items.extend(self.posts[a])
28 return sorted(items, reverse=True)[:k]
29
30
31class StockView:
32 def apply(self, event):
33 eid, book, version, qty = event
34 if eid in self.seen:
35 return "duplicate"
36 self.seen.add(eid)
37 if version <= self.version.get(book, 0):
38 return "stale"
39 self.version[book], self.stock[book] = version, qty
40 return "applied"
41```
42
43All checks pass: groups receive every event while members split them; push, pull and hybrid produce identical feeds for 400 readers, with the hybrid doing 44% fewer writes than push and 66% fewer reads than pull; the stock view ends at 8 after a stale and a duplicate event; the outbox keeps the order and its event together and republishes after a relay crash; both dual-write failures are reproduced. The full file is in `code/ch23_pubsub.py`.
1Show the Level 4 solution
21. **Ghost event:** the publish happens **inside** the database transaction, but the broker isn't part of that transaction. The event goes out immediately. Then the stock reservation fails, the transaction **rolls back**, and order 883 is never saved. The SMS service already received `order_placed` and sends the confirmation.
32. **Moving the publish after the commit** means the event goes out only for saved orders. But if the service crashes (or the broker is unreachable) **between** the commit and the publish, the event is **lost**: a real order with no SMS, no points, no packing slip.
43. **The outbox:**
5
6```python
7def place_order(db, order):
8 with db.transaction():
9 db.insert("orders", order)
10 db.insert("stock_reservations", reserve(order))
11 db.insert("outbox", {"event_id": f"order_placed:{order.id}",
12 "topic": "order_placed",
13 "payload": {"order_id": order.id}})
14 # a relay (or CDC) publishes outbox rows and marks them sent;
15 # subscribers de-duplicate by event_id
16```
17
18Now the event exists if and only if the order does, and it's published at least once.
1Show the Level 5 (sketch) solution
21. **Hybrid.** Authors under a threshold (say 100,000 followers) fan out on write to followers' feed lists; above it, releases are pulled at read time and merged. With 10,000 releases a day, mostly from small authors, pushes are cheap; the few huge authors cost one write each.
32. **Storage:** per-reader feed lists of **book IDs** (not full book data) in Redis or a wide-column store, capped at the latest 500 entries. 20 million readers × 500 IDs × 8 bytes ≈ **80 GB** before overhead, sharded by reader ID. Book details come from a cached catalogue lookup. Inactive readers' feeds can expire and be rebuilt on demand.
43. **Publishing:** the catalogue service writes the book and an outbox row in one transaction; CDC or a relay publishes `book_released` keyed by author ID.
54. **Duplicates and disorder:** the fan-out worker de-duplicates by event ID (feed insertion keyed by book ID is idempotent anyway); updates to a release (a corrected date) carry a version, and older versions are ignored.
65. **Other subscribers**, each its own consumer group: push notifications for followers (Chapter 33), search indexing (Chapter 19), recommendations, and analytics. Each can fail, lag or be redeployed without affecting the others or the catalogue service.