1Show the Level 1 solution
2**A.**
31. **SSE**: one-way, frequent updates.
42. **WebSocket**: both sides send often.
53. **Mobile push**: the app isn't running.
64. **Short polling** every 30 to 60 s: rare, not urgent.
75. **SSE**: one-way streaming over HTTP, which is how most AI chat UIs stream text.
89**B.**
101. **False.** SSE is server to client only; the browser sends with normal requests.
112. **False.** The connection lives on one server; moving means the client reconnects.
123. **False.** No delivery guarantee; the app must fetch the real state.
134. **True.** Average delay ≈ interval ÷ 2, and requests ∝ 1 ÷ interval.
145. **False.** That causes a thundering herd; use backoff and jitter.
1Show the Level 2 solution
21. 100,000 ÷ 5 = **20,000 requests a second**. Per order: 20 min = 1,200 s ÷ 5 = 240 requests for 5 updates, so 235 ÷ 240 ≈ **98% empty** (exactly what the code shows).
32. About **half the interval**: **2.5 s** at 5 s, **15 s** at 30 s. (The code measured 2.8 s and 16.8 s for this order's update times.)
43. 1,000,000 × 20 KB = **20 GB**. 1,000,000 ÷ 100,000 = **10 gateways**; run about **12** so that losing one (or deploying one) leaves room for its 100,000 clients.
54. 1,000,000 ÷ 30 ≈ **33,000 pings a second**. Dead after 3 missed: about **90 seconds**.
65. 100,000 ÷ 30 ≈ **3,300 a second** on average (the code's random spread peaked at about 3,400), against **100,000 in one second** with no jitter: about **30× lower**.
1Show the Level 3 solution
2Tested solution (Python 3), the key parts:
34```python
5def short_polling(event_times, interval, duration):
6 requests = empty = 0
7 delays, pending = [], sorted(event_times)
8 t = interval
9 while t <= duration:
10 requests += 1
11 got = [e for e in pending if e <= t]
12 if not got:
13 empty += 1
14 delays += [t - e for e in got]
15 pending = [e for e in pending if e > t]
16 t += interval
17 return requests, empty, sum(delays) / len(delays)
181920def sse_format(event_id, event, data):
21 return f"id: {event_id}\nevent: {event}\ndata: {data}\n\n"
222324class OrderEvents:
25 def __init__(self):
26 self.log = [] # (id, status)
2728 def publish(self, status):
29 self.log.append((len(self.log) + 1, status))
3031 def since(self, last_event_id):
32 return [e for e in self.log if e[0] > last_event_id]
333435class FixedTracker:
36 def __init__(self):
37 self.seen, self.last_id = [], 0
3839 def receive(self, events):
40 for i, s in events:
41 if i > self.last_id: # skip duplicates
42 self.seen.append(s)
43 self.last_id = i
4445 def reconnect(self, server):
46 self.receive(server.since(self.last_id)) # like Last-Event-ID
47```
4849All checks pass: 240 polls with 235 empty at 5 s; 45 long polls with no delay; SSE events round-trip; the buggy tracker misses "rider assigned" and "out for delivery" while the fixed one gets all five once; jitter cuts the reconnect peak from 100,000 to about 3,400 a second; the silent connection is detected. The full file is in `code/ch21_realtime.py`.
1Show the Level 4 solution
2**The bug:** on reconnect, the tracker just **subscribes to new events**. Anything published **while it was disconnected** is lost forever. Here, the phone lost signal after "packed"; "rider assigned" and "out for delivery" were sent during the gap; when it reconnected, only "delivered" arrived. It also doesn't de-duplicate, so a replayed event would be shown twice.
34**The fix:** remember the last event ID; on reconnect, ask for everything after it; skip anything already seen:
56```python
7class Tracker:
8 def __init__(self, socket):
9 self.socket, self.statuses, self.last_id = socket, [], 0
1011 def on_message(self, event):
12 event_id, status = event
13 if event_id <= self.last_id:
14 return # duplicate: ignore
15 self.statuses.append(status)
16 self.last_id = event_id
1718 def on_disconnect(self):
19 self.socket = connect_again(last_event_id=self.last_id) # server replays the gap
20```
2122With SSE, the browser sends `Last-Event-ID` for you; the server still has to replay from it. For long gaps, simply re-fetch the order's current state.
1Show the Level 5 (sketch) solution
21. **Transports:** status and rider position on the open tracking screen over **SSE** (one-way; a WebSocket only if rider chat is added). Background alerts ("out for delivery", "delivered") via **APNs/FCM**, as nudges. Every screen open starts with a **GET** of the current state.
32. **Connection layer:** event-driven SSE gateways behind a load balancer configured for long-lived connections. 100,000 trackers at an assumed 100,000 per gateway means 1, so run **3** for headroom and failure. The order service and the location service publish to a **pub/sub backplane** (per-order channels); each gateway subscribes for the orders its clients watch.
43. **Signal lost for 2 minutes:** heartbeats time out; the client reconnects with backoff and jitter, sending the last event ID; the gateway replays status events after it from a short per-order buffer (or the order table). Rider positions aren't replayed: only the **latest** position matters, so the client gets the current one.
54. **Deploys:** drain one gateway at a time: stop new connections, send "reconnect", wait, shut down. Business logic lives in other services, so most deploys don't touch gateways.
65. **Rider positions:** riders send positions every 4 s to the location service (Chapter 18), which keeps the latest in memory. Only riders **on an active, watched delivery** publish to that order's channel, throttled to one update per 4 s; nobody else's map is updated.