Design live cricket score fan out
Last over of an IPL final. Tens of millions of people are watching a number that changes about once every thirty seconds, and every one of them wants it within a second of the ball being bowled.
The write rate is almost nothing. A few events per over from one scorer. The read rate is one of the largest concurrent workloads anyone runs. That ratio, roughly one write to a hundred million reads, is unlike anything else in this course, and it inverts the usual instincts: caching is easy here, and the hard part is the connection layer.
30 million people watching one match. Would you push updates over open connections, or let clients poll every few seconds? Name the cost that decides it.
What you are building
- Live score, wickets, overs and the current ball. A small document, a few hundred bytes, updated a few times a minute.
- Deliver an update within about a second of the event. Later than the crowd noise next door and the product has failed.
- Handle tens of millions of concurrent viewers. Concurrent, and arriving in a spike when a big wicket falls.
- Correct a score after a review. Scores go backwards sometimes, and a design that assumes append only breaks.
- Peak 30 million concurrent viewers, reached within minutes.
- Under 1 second from scorer input to a phone screen at p95.
- A viewer must never see the score go backwards except for a real correction.
- Cost per viewer matters. This runs for six weeks a year and idles the rest.
- Video streaming. Several orders of magnitude more bandwidth and a completely different design.
- Commentary text and social feeds. Same delivery layer, much larger payloads.
- Betting odds, which have latency and correctness requirements that would dominate the discussion.
The numbers
This is the page where the napkin math genuinely decides the architecture rather than confirming it.
Polling asks 6,000,000 times a second for a value that changes twice a minute, and each ask carries more header than payload. Push sends only when something happens. The catch is the other row: push needs about 300 servers holding connections open, and those servers exist whether or not anything is happening.
Drag the poll interval down to two seconds and watch the multiplier. Then remember that during the last over, users refresh manually, which is polling with no interval at all.
Push or poll
A poll costs roughly 600 bytes of request and response headers to deliver 400 bytes that usually have not changed. At 30 million viewers on a 5 second interval, that overhead alone is most of your bandwidth bill, and it is paid continuously whether or not a ball has been bowled. Push pays per event. The crossover is not close.
The design
The shape to notice is that the multiplication happens as late as possible. One event reaches a few hundred edge servers, and only inside those servers does it become tens of millions of messages. Fanning out any earlier means moving that volume across the network instead of within a process.
Deep dive one: what the edge server actually does
At 100,000 connections on one machine, the work per event is 100,000 writes. Doing that naively is the difference between a machine that copes and one that falls over.
That last point is the design decision that makes everything else possible. Send state, not events. A viewer who misses a message is not broken, they are just briefly one ball behind, and the next message fixes them with no replay, no acknowledgement, and no per viewer state to track.
Deep dive two: the spike is the workload
Traffic here is not steady. A wicket falls, everyone opens the app, and connections arrive in a wall.
Worse is the reconnect wave. If an edge server dies holding 100,000 connections, all of them reconnect at once. Without randomised backoff on the client, they arrive together at the remaining servers, push those closer to their limit, and can start a cascade.
Three defences, all cheap:
Randomised backoff on the client, which spreads a reconnect wave over minutes instead of seconds. The highest value code in this system lives on the phone.
Admission control at the edge. When a server is near capacity, refuse new connections immediately with a pointer elsewhere rather than accepting them and degrading everyone.
Degrade to polling. If the push tier cannot take a viewer, serve them the CDN snapshot. They get a score three seconds late instead of no score at all, and the CDN capacity is already there for the fallback path.
Deep dive three: the score that goes backwards
Third umpire overturns a decision. The score you already delivered is wrong.
Every update carries a monotonically increasing version from the ingest service, and clients ignore anything with a version lower than what they hold. That alone fixes the common ordering problem, where a retried or delayed message arrives after a newer one.
A correction is a new update with a higher version and a lower score. The version keeps increasing even though the score decreases, which is exactly why the version is a separate field and not the score itself. Clients that treat “score only goes up” as an invariant will show a stale score forever after the first review, and it will be a support ticket nobody can reproduce.
Break it
The API
Trade-offs
| Choice | What you gain | What you pay | Pick it when |
|---|---|---|---|
| Push over polling | Sub second delivery, and bandwidth proportional to events rather than to viewers times poll rate. | A stateful connection fleet sized for peak concurrency that idles between matches. | Latency is part of the product. For a score during a final, it is the product. |
| Full state per update over deltas | A dropped message is self healing, there is nothing to replay, and the server keeps no per viewer position. | Slightly larger payloads, which is nothing when the state is a few hundred bytes. | Small state that changes wholesale. Deltas only pay off for large documents. |
| Server sent events over WebSocket | Simpler, plain HTTP, automatic browser reconnect, and it passes through proxies that block upgrades. | One directional, so anything the client needs to send takes a separate request. | The data genuinely flows one way, which for scores it does. |
| CDN polling as a fallback tier | The push tier can shed load into it instead of failing, and it costs nothing when unused. | Two delivery paths to build and keep consistent. | Any system where peak is many times the average and graceful degradation is worth more than uniform latency. |
Interview replay
Checkpoint
1. Why is polling so much more expensive than push here?
2. Why send the full score in every update rather than a delta?
3. An edge server holding 100,000 connections dies. What is the danger?
The defining ratio is one write to roughly a hundred million reads, so this is a fan out problem and not a storage problem. I would push over server sent events to engaged viewers and keep CDN backed polling as both the default for casual viewers and the fallback under stress, because polling asks constantly for a value that changes twice a minute and pays more in headers than payload. One event goes to a few hundred edge servers, and only inside each server does it become a hundred thousand socket writes, so the multiplication happens as late as possible. Each server serialises once and reuses the bytes, and drops updates for connections that cannot keep up rather than blocking, which is safe because every update carries full state rather than a delta, so a missed message self corrects. Updates carry an increasing version so a review that lowers the score still moves forward. And the failure I would design for is an edge server dying: a hundred thousand synchronised reconnects, handled with randomised client backoff, admission control, and degrading to the polling path rather than refusing service.
