← all work

Software · 05 / 06 · Python · WebSockets

Real-Time Event Streaming

A live cricket scoreboard feed where 1,000 clients receive every event exactly once and in order, through crashes and reconnects.

5M / 5Mdeliveries, every client got every event
14,069reconnects survived under chaos
5 msp95 live delivery latency
0duplicate or out-of-order events

Why I built this

In the real world

Live scores, delivery tracking, stock tickers and chat all push updates to millions of phones at once, and mobile connections drop all the time. During an IPL match, a score that skips a wicket or shows it twice breaks trust faster than one that's a second late.

What I wanted to find out

How do you push one update to a thousand screens at once, when some are on fast Wi-Fi and some are on a train? I wanted to find out how to stop one slow phone from holding everyone else back, and how a dropped phone catches up without missing a ball.

What I built

A live event feed, demoed as a cricket scoreboard. Producers publish events, PostgreSQL stores them in order, and every WebSocket subscriber on any of three servers receives each event exactly once, in order.

Then I attacked it: random client drops, killed server instances and killed Redis connections, with 1,000 clients connected and every sequence number audited.

How it works

ProducersPOST events with an event_id
nginxload balancer
3 instancesFastAPI + WebSockets
PostgreSQLordered log + snapshots
Redis pub/subbest-effort fan-out

Each instance tracks the last sequence number it delivered. Redis is allowed to lose, duplicate or reorder messages: gaps are filled from PostgreSQL and duplicates dropped.

How it stays correct

Ordering

Appends lock the stream's row, so sequence numbers are assigned in commit order.

Race-free resume

A reconnecting client is registered before its history replays, and live events are buffered meanwhile.

Backpressure

Every client has a bounded queue. One that falls behind is cut off and resumes from its cursor; nobody else slows down.

One recovery path

Drops, crashes, deploys, slow consumers and gaps all end the same way: reconnect and resume from the cursor.

Measured

Load
1,000 WebSocket clients, 3 instances, 100,000 events, continuous chaos
Chaos
Random client drops, 5 instance SIGKILLs, 8 Redis pub/sub kills
Latency
p95 5 ms, p99 14.5 ms, producer send to subscriber receive
Headroom
About 50,000 deliveries/s without chaos
Browser client
React + TypeScript client resumes from its cursor; in Playwright chaos tests every tab applied 1,502 / 1,502 events once