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.
- Python 3.12
- asyncio
- FastAPI
- WebSockets
- PostgreSQL
- Redis pub/sub
- React
- TypeScript
Why I built this
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.
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
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
Appends lock the stream's row, so sequence numbers are assigned in commit order.
A reconnecting client is registered before its history replays, and live events are buffered meanwhile.
Every client has a bounded queue. One that falls behind is cut off and resumes from its cursor; nobody else slows down.
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