Courses › System Design › Question Breakdowns
Design Facebook Live Comments
SSE fan-out, per-video pub/sub routing, sampling for 10M-viewer streams and history for late joiners.
Facebook Live Comments shows viewers of a live video the comments other viewers are posting, in real time. A popular stream can have millions of viewers and thousands of comments per second. It's a question about real-time fan-out at a massive ratio of readers to writers: how a comment reaches every viewer within a couple of seconds, how you route updates only to servers that have viewers of that video, and what you do when there are far more comments than anyone can read.
Requirements
Functional
- Viewers can post a comment on a live video
- Viewers see new comments in near real time while watching
- Viewers joining late see recent comments and can scroll back further
Out of scope: the video stream itself, reactions, moderation internals, replies to comments.
Non-functional
- Scale: ~1M concurrent live videos; the biggest have ~10M concurrent viewers
- Latency: a comment appears for other viewers within ~2 s
- Availability over consistency: it's fine if viewers see comments in slightly different orders or miss a few on a huge stream
- Read-heavy: reads outnumber writes by orders of magnitude
[!TIP] Fan-out is the whole problem One comment on a 10M-viewer stream is 10M deliveries. Say this early: the design is about getting each comment to only the servers that hold viewers of that video, then letting each server fan out to its local connections — and about sampling when there are more comments than humans can read.
Capacity estimates
Core Entities
- Live video — id, broadcaster, status
- Comment — id (time-sortable), video id, author, text, created time
- Viewer connection — video id, server, connection
API
POST /v1/videos/{videoId}/comments { text } → 201 { commentId }
GET /v1/videos/{videoId}/comments?before= → comments[] (history, paginated)
GET /v1/videos/{videoId}/comments/stream (SSE) → new comments as they arriveHigh-Level Design
user = client "Viewers" @0,1
lb = service "Load Balancer" @1,1
write = service "Comment Service" @2,0
db = store "Comments DB\n(by videoId, time)" @3,0
pubsub = queue "Pub/Sub\n(channel per video)" @3,1
rt = service "Realtime Servers\n(SSE connections)" @2,2
reg = cache "Subscription Registry" @3,2
user -> lb
lb -> write : post
lb <-> rt : stream
write -> db
write -> pubsub
pubsub -> rt
rt -> reg : subscribe video- Post: the comment service validates and stores the comment, then publishes it to the video's channel.
- Subscribe: when a viewer opens the stream, their realtime server subscribes to that video's channel (if it isn't already).
- Fan out: each subscribed realtime server receives the comment once and pushes it to all its local viewers of that video.
- History: late joiners load recent comments from the database (or a cache of the last few hundred per video).
Deep Dives
1. Push vs poll
See Real-time Updates.
2. Routing comments to the right servers
Viewers of one video are spread over many realtime servers. Options:
- Broadcast every comment to every server: with thousands of servers and 100k comments/s, each server processes all comments for videos it doesn't care about. Doesn't scale.
- Pub/sub with a channel per video *(recommended)*: servers subscribe only to videos their viewers are watching. Redis pub/sub (sharded by video ID) or a dedicated dispatcher works. Kafka with a topic per video doesn't — a million topics is too many; if you use Kafka, partition by video ID and let a dispatcher tier route.
- Co-locate viewers of the same video on the same servers (route connections by video ID with consistent hashing). This reduces the number of servers each comment goes to, but a 10M-viewer video still needs many servers, so combine it with pub/sub.
See Broadcasting with Pub/Sub.
3. The 10-million-viewer stream
5k comments/s is unreadable — the chat would scroll faster than anyone can see. Don't deliver everything:
- Sample: deliver at most ~10–20 comments per second per viewer. Pick by quality — comments from friends, verified accounts and the broadcaster first, then a random sample of the rest.
- Batch: realtime servers send one message per second containing the selected comments instead of one message per comment.
- Tiered fan-out: comments flow from the pub/sub to a layer of edge fan-out nodes, each serving many realtime servers, so no single node pushes to thousands of downstream servers.
[!WARNING] Delivering every comment to every viewer It's the natural first design and it fails by orders of magnitude for big streams. Interviewers expect you to notice and to sample or rank.
4. Storing comments
- Write path: ~100k comments/s overall, append-only, read by
(videoId, time)range. Cassandra or DynamoDB with partition keyvideoId(bucketed by time for huge streams) and a time-sortable comment ID as the clustering key. See Cassandra. - Keep the last few hundred comments per video in Redis for late joiners, so loading a hot video's history never hits the database.
- After the stream ends, comments are read rarely; they can move to cheaper storage.
5. Ordering and duplicates
- Comment IDs are time-sortable (Snowflake-style), so clients sort by ID; small differences between viewers are acceptable.
- A client might receive a comment both from history and from the stream; it deduplicates by ID.
- On reconnect, the client asks for comments after the last ID it saw.
6. Connection management and failures
- Realtime servers hold hundreds of thousands of idle connections each; use event-driven servers and tune kernel limits.
- If a realtime server dies, its clients reconnect (with jittered backoff to avoid a thundering herd) to another server, which subscribes to the video and backfills from the cache.
- Subscriptions are cleaned up when a server's last viewer of a video disconnects.
What's expected at each level
- Mid-level: posting and storing comments, pushing new comments to viewers over SSE or WebSockets instead of polling.
- Senior: per-video pub/sub so only relevant servers receive each comment, the history cache for late joiners, reconnect and deduplication handling.
- Staff: the huge-stream problem with sampling, ranking and batching, tiered fan-out, connection-level scaling and recovery, and trade-offs between co-locating viewers and pub/sub routing.