1"""Chapter 41 (case study: live streaming at scale, live cricket) deep-dive code.
21. Live latency: how segment length and player buffer set the delay behind the real match.
32. CDN at peak: egress, segment requests, and what reaches the origin with a shield.
43. Pre-scaling: reactive autoscaling vs scaling ahead of a known spike.
54. Live score sync: polling a cached score vs per-user pushes; avoiding spoilers.
6Run: python3 ch41_live.py"""
7import math
8
9
10# ---- 1. latency ----------------------------------------------------------------------
11
12def glass_to_glass(segment_s, segments_buffered, encode_s=2, cdn_s=1):
13 """Capture/encode + one segment being filled + what the player buffers + delivery."""
14 return encode_s + segment_s + segment_s * segments_buffered + cdn_s
15
16
17# ---- 2. CDN ------------------------------------------------------------------------
18
19def egress_tbps(viewers, avg_mbps):
20 return viewers * avg_mbps / 1e6
21
22
23def segment_requests_per_s(viewers, segment_s):
24 return viewers / segment_s # + one playlist refresh each, in reality
25
26
27def origin_requests_per_segment(renditions, shields, collapse=True):
28 """With request collapsing at each shield, each new segment reaches the origin once per shield per rendition."""
29 return renditions * shields if collapse else None
30
31
32# ---- 3. pre-scaling ----------------------------------------------------------------
33
34def simulate(demand, per_server, start_servers, boot_min, mode, target_util=0.7, prescale_to=None):
35 """Minute by minute. Reactive: add servers when utilisation > target; they arrive after boot_min.
36 Pre-scaled: capacity is raised to prescale_to before the match. Returns minutes of overload and users dropped."""
37 servers, pending, overload_min, dropped = start_servers, [], 0, 0
38 if mode == "prescaled":
39 servers = prescale_to
40 for t, users in enumerate(demand):
41 servers += sum(n for ready, n in pending if ready == t)
42 pending = [(r, n) for r, n in pending if r != t]
43 capacity = servers * per_server
44 if users > capacity:
45 overload_min += 1
46 dropped += users - capacity
47 if mode == "reactive" and users > capacity * target_util:
48 needed = math.ceil(users / (per_server * target_util)) - servers - sum(n for _, n in pending)
49 if needed > 0:
50 pending.append((t + boot_min, needed))
51 return overload_min, dropped
52
53
54# ---- 4. score sync -----------------------------------------------------------------
55
56def score_poll_rps(viewers, every_s):
57 return viewers / every_s
58
59
60def origin_score_rps(pops, ttl_s):
61 return pops / ttl_s
62
63
64def score_to_show(events, video_time):
65 """Show the latest score whose match time is not ahead of what this viewer's video is showing."""
66 shown = [e for e in events if e[0] <= video_time]
67 return shown[-1][1] if shown else None
68
69
70# ---- checks ------------------------------------------------------------------------
71
72def run_checks():
73 # 1. latency
74 assert glass_to_glass(6, 3) == 27 # 6 s segments, 3 buffered: ~27 s behind TV
75 assert glass_to_glass(2, 3) == 11 # 2 s segments: ~11 s
76 assert glass_to_glass(1, 2) == 6 # low-latency (partial) segments: a few seconds
77
78 # 2. CDN at 25M concurrent viewers
79 assert egress_tbps(25_000_000, 2.0) == 50.0 # Tbit/s at 2 Mbit/s average
80 assert segment_requests_per_s(25_000_000, 4) == 6_250_000
81 assert origin_requests_per_segment(6, 10) == 60 # 6 renditions x 10 shield sites, per new segment
82
83 # 3. a star walks out to bat: 5M -> 25M viewers in 10 minutes
84 demand = [5_000_000] * 10 + [5_000_000 + 2_000_000 * i for i in range(1, 11)] + [25_000_000] * 20
85 reactive = simulate(demand, per_server=50_000, start_servers=150, boot_min=6, mode="reactive")
86 prescaled = simulate(demand, per_server=50_000, start_servers=150, boot_min=6, mode="prescaled",
87 prescale_to=math.ceil(25_000_000 * 1.3 / 50_000))
88 assert reactive[0] >= 4 and reactive[1] > 10_000_000
89 assert prescaled == (0, 0)
90
91 # 4. score sync
92 assert score_poll_rps(25_000_000, 10) == 2_500_000 # served by the CDN
93 assert origin_score_rps(200, 2) == 100 # 200 PoPs, 2 s TTL: tiny origin load
94 events = [(100, "IND 120/2"), (130, "IND 126/2 (SIX!)"), (160, "IND 126/3 (OUT)")]
95 assert score_to_show(events, video_time=135) == "IND 126/2 (SIX!)" # viewer 30 s behind: no spoiler
96 assert score_to_show(events, video_time=165) == "IND 126/3 (OUT)"
97
98 return {"latency_s": (27, 11, 6), "reactive_overload": reactive, "prescaled": prescaled}
99
100
101if __name__ == "__main__":
102 print("All checks pass.", run_checks())