Design Live Comments for a Video Stream

MediumSocial & FeedsWebSocketsPub/SubFan-outReal-Time

Problem

Design the comment feature that appears beside a live video, like the comment stream on Facebook Live or YouTube Live. Viewers post short text comments while watching, and every other viewer of the same stream sees new comments within about two seconds. Viewers who join late see the most recent comments, and moderators can remove a comment so it disappears for everyone. The platform runs 50,000 concurrent live streams with 20 million concurrent viewers in total, and the largest single stream can reach 2 million viewers.

  • Must every viewer see every comment, or is it acceptable to show a sample when a stream is very busy?
  • Are comments ordered strictly by server time, or is approximate ordering acceptable?
  • How long are comments kept after the stream ends? Do we need to replay them in sync with the recorded video?
  • Are reactions (hearts, likes) part of the same system or a separate counter feature?
  • Is there a per-user rate limit on posting, and do we need spam and profanity filtering before display?
  • Do comments need to be visible to the poster instantly, even before the server confirms?

Items in bold are existing requirements. The rest are suggestions. Confirm with the interviewer whether they are relevant first.

  • Post a text comment to a live stream.
  • Deliver new comments to all current viewers of that stream in near real time.
  • Show recent comments to a viewer who joins mid-stream.
  • Let moderators delete a comment and hide it for all viewers.
  • Filter spam and abusive text before a comment is broadcast.
  • Store comments so they can be replayed alongside the recorded video.

Items in bold are existing requirements. The rest are suggestions. Confirm with the interviewer whether they are relevant first.

  • End-to-end latency under 2 seconds from post to display for 99% of viewers.
  • Support 20 million concurrent connections across 50,000 streams, with up to 2 million viewers on one stream.
  • Write volume: if 1% of viewers comment once a minute, that is about 3,300 comments per second on average, and a big moment can spike one stream to 10,000 per second.
  • Read fan-out is the real cost: one comment on a 2 million viewer stream becomes 2 million deliveries, so total deliveries can exceed 100 million messages per second at peak.
  • Comment loss is tolerable under overload (this is ephemeral chatter, not payments), but the system must never fall behind by minutes.
  • Storage: 3,300 comments per second at 300 bytes is about 85 GB per day, kept for 90 days.

Viewers hold a WebSocket connection to a gateway tier, and each gateway subscribes to a pub/sub channel per stream only when at least one of its connections is watching that stream. A comment service validates, filters, and persists each comment, then publishes it to the stream's channel, where every subscribed gateway pushes it to its local viewers. For very large streams the service samples comments before publishing so the delivered rate stays readable and the fan-out stays bounded.

Loading diagram...

Watch a stream. The client opens a WebSocket to a gateway and sends subscribe(streamId). The gateway records the connection in its in-memory map and, if this is its first viewer of that stream, subscribes to the stream's pub/sub channel. The client then fetches the last 50 comments from the recent comments cache so the panel is not empty.

Post a comment. The client sends POST /streams/:id/comments. The comment service checks the user's rate limit, runs the text through the moderation filter, assigns a server timestamp and a unique ID, writes the comment to the comment store, and appends it to the stream's recent list. It then publishes the comment to the stream channel. The poster's client shows the comment immediately and reconciles with the server ID when it arrives.

Fan-out. Every gateway subscribed to the channel receives the comment once and writes it to each local connection watching the stream. Gateways batch comments in 200 ms frames, which cuts per-socket write overhead without hurting perceived latency.

Delete a comment. A moderator calls DELETE /comments/:id. The service marks the comment deleted in the store, removes it from the recent list, and publishes a tombstone event on the same channel so clients remove it from view.

Holds long-lived viewer connections and pushes comments to them. It is the only tier that knows which sockets are watching which stream.

  • Go or Rust servers using an event loop, each holding around 200,000 idle connections.
  • An L4 load balancer that spreads new connections by least connections.

Diagram

Loading diagram...

Scalability

20 million connections at 200,000 per host needs about 100 gateway hosts plus headroom. Because each gateway subscribes to a channel only once, a stream with 2 million viewers spread over 100 gateways costs the pub/sub layer 100 deliveries per comment, not 2 million. The gateway does the final multiplication in memory.

Tradeoffs

WebSockets beat long polling because they avoid a request per update and keep latency low. Server-Sent Events would also work for the downstream path, but posting still needs a separate request, and many mobile networks handle WebSockets just as well. Keeping posts on a plain HTTP API instead of the socket keeps the gateway simple and lets the write path scale independently.

Stateless service that validates, filters, stores, and publishes comments, and applies sampling for busy streams.

  • Stateless HTTP service autoscaled on request rate.
  • Token bucket rate limiting per user in Redis.

API

  • POST /streams/:id/comments with { text } returns { commentId, createdAt }.
  • GET /streams/:id/comments/recent?limit=50 returns the latest comments.
  • GET /streams/:id/comments?from=&to= returns comments by video offset for replay.
  • DELETE /comments/:id hides a comment (moderator only).

Scalability

Sampling protects both the fan-out layer and the viewer. No human reads 10,000 comments per second, so above a threshold (say 30 per second) the service publishes a ranked sample: comments from the streamer, moderators, friends of many viewers, and verified accounts first, then a random slice. Every comment is still stored, so nothing is lost for replay.

Tradeoffs

Persisting before publishing adds a few milliseconds but guarantees that a comment viewers saw can be moderated and replayed. Publishing first and writing asynchronously would be faster, but a crash could show a comment that moderators can never find.

Pub/sub layer that routes each comment to the gateways watching that stream.

  • Redis Pub/Sub sharded by stream ID, or NATS, both of which offer fire-and-forget delivery with low latency.

Scalability

Shard channels across pub/sub nodes by hashing the stream ID. Total throughput is comments per second times subscribing gateways per stream, which stays in the low millions of messages per second even at peak. One extremely hot stream can be given a dedicated shard.

Tradeoffs

Kafka was rejected for live delivery: per-partition consumer groups do not map well to tens of thousands of short-lived per-stream channels, and its disk-backed durability is not needed for a feed that is also stored elsewhere. At-most-once delivery means a gateway restart drops a few comments, which viewers do not notice.

Durable storage of every comment for moderation, replay, and history.

  • Cassandra or another wide-column store tuned for append-heavy writes.
  • Redis list per stream for the recent comments cache.

Data Model

  • comments: stream_id (partition key), created_at and comment_id (clustering keys), user_id, text, video_offset_ms, is_deleted.
  • Redis recent:{streamId} capped at 200 entries with LPUSH and LTRIM.

Scalability

Partitioning by stream keeps each stream's comments together for replay queries. A single stream producing 10,000 writes per second can create a hot partition, so very busy streams add a time bucket (for example, one minute) to the partition key. A 90 day TTL removes old data automatically.

Tradeoffs

A relational database would make moderation queries easy, but the write rate and the append-only, time-ordered access pattern fit a wide-column store far better. Moderation search runs on a separate index fed by change data capture.

Synchronous check that blocks spam, abuse, and banned users before a comment is published.

  • Fast in-process checks for blocked words, links, and banned users.
  • A lightweight ML toxicity classifier served with a strict 50 ms timeout.

Tradeoffs

Running the classifier inline delays every comment slightly, so it fails open on timeout and a slower asynchronous pass can tombstone anything it catches later. Holding all comments for human review would make the feature feel dead during a live event.

Related questions