Common Pattern · Real-time Systems

Real-time Updates

How to push data from server to client, how to route it to the right server, and how to pick the simplest option that still meets the requirement.

Contents
  1. Problem & two hops
  2. Networking 101
  3. L4 vs L7
  4. Simple polling
  5. Long polling
  6. SSE
  7. WebSockets
  8. WebRTC
  9. Hop 1 flow
  10. Hop 2 routing
  11. Putting it together
  12. Deep dives
  13. Recall
  14. Revision

Real-time updates deliver data changes from servers to clients the moment they happen — chat messages, driver notifications, live comments, collaborative edits. A lot of candidates bungle this, not because they’re bad engineers, but because real-time systems touch networking concepts many of us never had to build from scratch. The trick that makes it manageable: break it into two independent hops.

01The problem, and the two hops

Motivation → the framing that organizes everything

Consider a collaborative editor like Google Docs. When one user types a character, every other viewer needs to see it within milliseconds — not 3–5 seconds later. The naive fix, every user constantly polling “any changes now? now? now?”, would crush your infrastructure with millions of requests every few milliseconds.

The root difficulty: standard HTTP is request-response. Clients ask, servers answer, the connection closes (or at best stays open waiting for another client-initiated request). That model isn’t built to let a server push updates on its own.

The two hops — the mental model for the whole pattern

Think about how data flows through the system, and real-time updates split into two distinct problems:

Hop 1 — server → client: how do I get an update from a server out to the connected client? This is a networking / protocol question.

Hop 2 — source → server: how does an update reach the specific server that the target client is connected to? This is tricky because the server connected to your client usually isn’t the one that generated the update.

Update source Server(client’s conn.) Client HOP 2 source → server HOP 1 server → client
Because these are a natural partition with different trade-offs, we solve them separately, then tie them together.
Reassurance for experienced candidates

These problems are often solved once by a specialized infra team, so plenty of engineers with 10+ years of experience have never built them. The interviewer’s baseline expectation here is usually low — the goal is to know the options and justify your choice, not to have built it before.

02Networking 101

Background → why HTTP overhead matters

In some sense these are just networking problems. Networks use a layered architecture (the OSI model) where each layer builds on the abstraction below it. Three layers matter most in interviews:

LayerProtocolsWhat it gives you
L3 · NetworkIPRouting/addressing, breaks data into packets, best-effort delivery. No guarantees — packets can be lost, duplicated, or reordered.
L4 · TransportTCP, UDPTCP is connection-oriented: set up a connection first, then get correct, in-order delivery (costs time, resources, bandwidth). UDP is connectionless: send to any IP with no setup, no ordering/delivery guarantee — “spray and pray.”
L7 · ApplicationDNS, HTTP, WebSockets, WebRTCApplication protocols built on top of TCP, abstracting data types for web apps.

The request lifecycle — and its hidden overhead

Typing a URL kicks off a carefully orchestrated sequence. First DNS resolves the domain to an IP, then:

  1. DNS resolution — domain → IP (one round trip; usually cached, but still overhead).
  2. TCP handshake (3-way) — SYN → SYN-ACK → ACK establishes the connection.
  3. HTTP request — the client sends its actual GET.
  4. Server processing — checks a DB, prepares a response.
  5. HTTP response — the data comes back.
  6. TCP teardown (4-way) — FIN → ACK → FIN → ACK closes the connection.
Why this matters for pollingfrom the video

For every single “has anyone typed anything new?” request, you’re potentially paying DNS resolution + a 3-way handshake + a 4-way teardown — and 90% of the time the answer is “nope, nothing new.” Multiply that overhead by millions of users and naive polling clearly won’t work. Seeing this overhead concretely is what makes the smarter protocols make sense.

Two points to carry forward

1. Latency: every round trip (including connection setup) adds latency before your application data even moves.

2. State: a TCP connection is state both sides must maintain. Without HTTP keep-alive, you repeat this setup for every request — significant overhead.

03L4 vs L7 load balancers

Prerequisite → shapes later protocol choices

Real systems put multiple servers behind a load balancer. The L4-vs-L7 distinction returns directly when we choose real-time protocols.

Layer 4 (transport)
  • Routes on IP/port; doesn’t inspect packet content.
  • Maintains a persistent TCP connection between client and server — as if the client has a direct TCP line to one backend.
  • Fast, minimal inspection; can’t route on app data.
  • Well-suited to persistent connections like WebSockets.
Layer 7 (application)
  • Understands HTTP; can route on URL, headers, cookies.
  • Terminates incoming connections and opens new ones to backends.
  • More CPU-intensive; more flexible/featureful.
  • Better for HTTP-based solutions like long polling.

Some L7 balancers explicitly support connection-oriented protocols like WebSockets, but generally L4 is better for WebSockets while L7 offers more flexibility for HTTP-based approaches. An L7 balancer doesn’t guarantee the same TCP connection per request — which is exactly the guarantee persistent protocols need.

04Hop 1a · Simple polling — the baseline

The default you should reach for first

The simplest approach: the client makes an HTTP request at a regular interval and the server responds with the current state. For a chat app, you constantly poll “what messages haven’t I received yet?”. It doesn’t technically qualify as real-time, but it’s the right starting point far more often than candidates think.

async function poll() {
  const response = await fetch('/api/updates');
  const data = await response.json();
  processData(data);
}
// Poll every 2 seconds
setInterval(poll, 2000);
Polling should be your defaultemphasized in the video

Too many candidates jump straight to WebSockets or complex pub/sub when polling every 2–5 seconds would work fine. Most interview problems don’t need sub-second updates — a stock price every 3 seconds or a dashboard every 5 is totally acceptable. Always start by asking “how real-time does this actually need to be?” — you can even ask the interviewer. If the answer is “pretty responsive,” polling is usually adequate. Save the complex stuff for genuine sub-second needs.

How to frame it in the interview

Give yourself a backdoor: “I’ll start with a simple polling approach so I can focus on X, but we can switch to something more sophisticated if we need to.” But do propose it before committing — if the interviewer is dead-set on WebSockets/SSE/WebRTC, you want to know early. To counter the “too slow / inefficient” objection, mention HTTP keep-alive set longer than the polling interval, so you reuse one TCP connection instead of re-establishing it each time.

Good for: anything not requiring very-low-latency updates, and short update windows (e.g. the LeetCode design). Costs: higher latency (up to interval + processing), limited frequency, more bandwidth, and connection churn with many clients.

05Hop 1b · Long polling — the easy upgrade

Clever hack on regular polling
Definition

Long polling: instead of the server immediately replying “no updates,” it holds the request open until data is available or a timeout occurs. It’s as if the server is just taking a long time to respond. Then it answers, the client immediately opens a new request, and the cycle repeats.

// Client-side of long polling
async function longPoll() {
  while (true) {
    try {
      const response = await fetch('/api/updates');
      const data = await response.json();
      processData(data);
    } catch (error) {
      console.error(error);
      await new Promise(r => setTimeout(r, 1000)); // back off on error
    }
  }
}

This cuts the wasteful “no updates” replies. But it has two costs. First, the held-open request is effectively state — and we generally want to minimize state. Second, it introduces extra latency for high-frequency updates, because each update needs the client to “call back” before the next one can be sent.

The latency cost, quantified

The webpage’s worked example (assume 100ms client↔server latency):

# two updates occur within 10ms of each other update 1 received: 100ms after it occurred # 1× network latency (unavoidable) update 2 received: up to 290ms after it occurred # 90ms to finish returning response 1 # + 100ms for the client's next request # + 100ms for that response

The video frames the same idea: for the second update you “ate about a round trip of latency” because you had to wait for the client to call back.

Watch out

Long polling is a great, low-complexity near-real-time solution, but not suitable for high-frequency updates (many updates near each other). Also: browsers limit concurrent connections per domain; monitoring is painful since requests hang around; and each infra hop must tolerate the long request — don’t let a load balancer hang up at 30s when your server holds for 60s (15–30s is a common interval that minimizes fuss).

Sweet spot

A long async process where you want to know the instant it finishes — like payment processing: long-poll for the payment status, then show the success page.

06Hop 1c · Server-Sent Events — the efficient one-way street

The frequently-best interview option for push
Definition

SSE extends long polling so the server can send a stream of data. Instead of a fixed Content-Length, it uses Transfer-Encoding: chunked to tell the client the response is an open-ended series of chunks. This turns one atomic request/response into a granular stream of events.

Why it beats long polling for frequencyfrom the video

With long polling, every request needed a single response, so you waited for the client to call back between updates. With SSE you can send multiple responses down the same HTTP response — each event pops up client-side like a push notification, with no call-back gap. The browser side is the built-in EventSource object, which also handles automatic reconnection.

// Client-side
const eventSource = new EventSource('/api/updates');
eventSource.onmessage = (event) => {
  const data = JSON.parse(event.data);
  updateUI(data);
};

// Server-side (Node/Express)
app.get('/api/updates', (req, res) => {
  res.setHeader('Content-Type', 'text/event-stream');
  res.setHeader('Cache-Control', 'no-cache');
  res.setHeader('Connection', 'keep-alive');
  const sendUpdate = (data) => res.write(`data: ${JSON.stringify(data)}\n\n`);
  dataSource.on('update', sendUpdate);
  req.on('close', () => dataSource.off('update', sendUpdate));
});

The key headers: Content-Type: text/event-stream, no caching (it’s real-time, theoretically uncacheable), and connection kept alive. A very popular modern use case is AI chat apps streaming tokens as they’re generated.

Watch out — the infra gapthe source uses SSE heavily and warns from experience

One-way only (server→client). Some proxies and load balancers don’t support streaming — they buffer the response until it completes, silently blocking your stream in a way that’s nasty and opaque to debug. You must ensure your load balancer and API gateways won’t close long-open connections. Browsers also cap concurrent connections per domain.

Reconnection with “last event ID”

SSE connections aren’t super long-lived (30–60s typical). The SSE standard includes a “last event ID” the browser sends on reconnect, so the server can replay everything the client missed while disconnected. Most interviewers aren’t familiar with SSE’s infra details and won’t grill you — unless the role is very frontend-centric.

07Hop 1d · WebSockets — the full-duplex champion

For high-frequency, bidirectional communication
Definition

WebSockets are essentially an abstraction over a TCP channel: bytes flow in either direction with no request/response pairing. They build on HTTP via an “upgrade” that switches an existing TCP connection’s L7 protocol (so you can reuse cookies/headers). You get TCP’s guarantees — ordering, delivery acknowledgement — and build your own message protocol on top.

You design APIs as messagesfrom the video

Because it’s a raw channel, in an interview you define your API in terms of the messages you send. Some have responses, some don’t — e.g. the server sends ticker update messages while the client sends subscribe. It probably makes sense for the server to ack a subscription, but maybe not — your choice depends on the use case.

// Client-side
const ws = new WebSocket('ws://api.example.com/socket');
ws.onmessage = (event) => handleUpdate(JSON.parse(event.data));
ws.onclose = () => setTimeout(connectWebSocket, 1000); // reconnect
Two infra rules the video stresses

Use an L4 load balancer. A WebSocket is basically a bare TCP connection; you can’t redirect WebSocket messages to different services the way an L7 balancer redirects HTTP. (L7 balancers that support WebSockets exist — fine to call out — but interviewers aren’t unusually sensitive to the choice.)

Terminate WebSockets early — usually into a dedicated WebSocket service at the front of your infra. That way not every backend service needs the same stateful connections, and the rest of the system stays stateless. A driver/rider service that everyone connects to also makes state easier to manage.

Watch out — statefulness & deployments

Unlike SSE (which reconnects every ~30s), a WebSocket may stay open for hours, so the infra must support it end-to-end. Deployments are a problem: on redeploy you either sever old connections and let clients reconnect (simpler, preferred) or migrate them. Handle missed updates on reconnect. Load balancing is harder because you must “stick with” each allocation — a “least connections” strategy helps avoid hotspots.

Don’t over-reach for WebSockets

Candidates adopt WebSockets too eagerly. Since you can always make separate requests for writes, a common pattern is SSE subscriptions for updates + simple HTTP POST/PUT for writes. Defer to SSE unless you have a specific need for bidirectional communication.

08Hop 1e · WebRTC — the peer-to-peer solution

The most esoteric — narrow, but sometimes exactly right

WebRTC enables direct peer-to-peer connections between clients — great for video/audio calls and some data sharing. It sounds odd to discuss on the backend, but setting up a P2P connection involves real backend responsibilities.

How the setup works

  1. Signaling server — clients connect here to discover peers and exchange connection info (ICE candidates). It’s lightweight, handles little bandwidth, and is only used in the initial handshake — so it needn’t be heavily latency-optimized. It can be regionalized/partitioned (e.g. never connect North America to Europe) or global.
  2. STUN (“Session Traversal Utilities for NAT”) — most clients don’t accept inbound connections for security, sitting behind NAT. STUN uses techniques like “hole punching” to establish a publicly routable address/port, shared via the signaling server.
  3. Direct connection — client A connects to client B over those public ports.
  4. TURN — fallback when STUN fails (e.g. behind a corporate firewall): relay traffic through a central server. It’s the least desirable mode because all the bandwidth now flows through your servers — the very thing P2P was avoiding.
Why usually avoid it — and when not tofrom the video

For most “peer-to-peer looking” scenarios, a WebSocket is preferable: you keep server-side control and dodge STUN/TURN servers and malicious clients connecting to each other. A server in the middle usually isn’t a big deal — except for the latency and bandwidth it costs. That exception is exactly where WebRTC wins: video conferencing (Google Meet) and multiplayer gaming, where a relay’s added latency and bandwidth would be prohibitively expensive.

A subtle point & a real-world example

The signaling server itself is effectively a real-time update system (peers must find each other and update connection info), so it needs WebSockets/SSE under the hood. WebRTC can also reduce server load when clients talk to each other frequently — Canva used it for presence/pointer sharing in its canvas editor, and it pairs well with CRDTs for collaborative editing.

09Hop 1 · The decision flow

How to choose, in interview terms
Am I truly latency sensitive?
No — 5s late is fine → Simple polling (start here; many “real-time” industrial apps are just polling behind the scenes)
Yes → Is the communication frequent & bidirectional?
No (~75% of cases are just server→client push) → SSE — often no extra infra, no load-balancer fuss
Yes → Do I have audio/video requirements?
Yes (video call, multiplayer game) → WebRTC
No → WebSockets
The message of the flowchartemphasized in the video

WebSockets are kind of a last resort. Despite everyone wanting to talk about them, you’ll usually find polling or SSE more appropriate — and simpler. Be careful about prematurely pulling the trigger on stateful real-time communication when it isn’t needed.

10Hop 2 · Getting updates to the right server

Source → server routing

With persistent connections (long-polling, SSE, WebSockets), each client is connected to one server, and that server sends its updates. The problem: to send a message to User C, we must know which server User C is connected to. Three patterns.

Pattern A · Pulling with simple polling

A pull model: the client polls, and the server queries a database for what’s new (e.g. “messages newer than my last one?”). This neatly sidesteps the routing problem — a server only pulls updates for the clients connected to it — and decouples the update source from the receiver via the DB.

Server-side polling as a kludgefrom the video

You can also have the server poll a DB at a higher frequency than a client could (it’s cheaper — inside the data center). But in interviews, server-side polling is less common and usually a hack/kludge when you’re short on time. If the interviewer set up a problem that needs real-time updates, they usually want the full near-zero-latency setup, not a faster poll. And watch the scale: 1M clients polling every 10s = 100k TPS of read volume.

Pattern B · Pushing via consistent hashing

Assign each user to a specific server so we always know where they’re connected. Simple modulo hashing (user % N) works when N is fixed but forces almost everyone to reconnect when you scale. Consistent hashing maps servers and users onto a ring; each user connects to the next server clockwise, so scaling only moves the users in affected ring segments.

Implementation piecesfrom the video

You need (1) a place to store and update the ring parameters — the video likes ZooKeeper, whose znodes are a lightweight way for servers to learn about each other; the ring is just an ordered server list + an agreed hash function, so each server can compute routing itself. (2) A way for users to reach the right server: either a middle server that stays subscribed to ZooKeeper, hashes the incoming user ID, and returns the right server; or the Redis-style approach where users randomly ping a server that either accepts them or redirects (“you actually belong to server two”). Both are fine.

Watch out — the orchestration is the hard part (and a deep-dive magnet)

Adding/removing nodes is a delicate balance. During a transition, servers must know both the old and new assignments until it completes, and messages may be sent to both the old and new server in the interim. Avoid a thundering herd: if you dump all users off a server at once (or take it down mid-move), you cause even more churn — and new connections are resource-heavy to spin up. Instead, formally signal the scaling event and drain connections slowly, giving the new server time to pick them up (back-pressure/feedback makes this more robust). Also: connection state is lost if a server fails. Because these deep dives open up, consider simplifying to Pub/Sub if real-time isn’t the core of the problem.

Pattern C · Pushing via Pub/Sub

A single service collects updates and broadcasts them to interested clients. The Pub/Sub service becomes the main source of state; your persistent connections now terminate at lightweight endpoint servers that just subscribe to topics and forward to clients.

How a message flows

Connect: client connects to any endpoint server (a plain load balancer, even random allocation, is fine). That server registers a subscription with the Pub/Sub service for the user’s topic.
Send: the update source publishes to the topic → subscribed endpoint servers receive it → each looks up its local WebSocket connection for that user and forwards the message. The Pub/Sub does the hard part; endpoint servers are dumb proxies.

Redis vs Kafka — decided by durability need

Redis Pub/Sub — lightweight, in-memory, no durability. Perfect “fire-and-forget” for something like a Yahoo-Finance stock ticker where you only care about the latest value (the full archive lives elsewhere).

Kafka — heavier, but full replay logs. When it’s important that all updates eventually reach users, store messages in the topic and replay what a reconnecting client missed. You don’t have to use Kafka for durability, but it makes life easier. (See the WhatsApp guide: Pub/Sub for low-latency chat delivery + a DB for message storage, doing both.)

Watch out

You don’t know whether subscribers are still connected (or when they drop). The Pub/Sub service is a single point of failure and bottleneck, adds a layer of indirection/latency (still typically <10ms), and creates many-to-many connections between Pub/Sub hosts and endpoint servers. Scale it with a Redis cluster (shard subscriptions by key); use least-connections load balancing for the endpoint servers.

Hop 2 decision

Ask “how stateful are my services?” If you already need heavy per-connection state (e.g. Google Docs: a document’s collaborative-editing state), consistent hashing keeps that state in one place. If you’re just passing small messages, Pub/Sub keeps endpoint servers lightweight and interchangeable — and it’s the better default for most problems.

11Putting the two hops together

Worked application choices
ApplicationHop 1 (server→client)Hop 2 (source→server)Why
Live dashboard Polling (if ≥5s OK) — or SSE None if polling — else Pub/Sub Push ticker updates; broadcast to all users subscribed to that ticker. Cheap and easy.
Collaborative editor WebSockets Consistent hashing Bidirectional + high-frequency. Put all (max ~100) users of a doc on the same server → simple broadcast, no extra hops, and it’s where CRDTs/operational transforms run.
Chat app SSE for receiving + REST for sends Pub/Sub (or consistent hashing) Bidirectional but not high-frequency; sends are relatively rare, so a REST call per send is fine.
Watch out — the celebrity chat roomfrom the video

For chat, beware a room that becomes huge. With consistent hashing, millions of users in one room all land on one server and overload it — the opposite of what you want. That pushes you toward Pub/Sub. Seeing this trade-off play out is the point: justify your choice per the workload.

Raise it proactively

Real-time needs appear in almost every interactive design. Don’t wait to be asked — flag it early: “messages need to be delivered instantly, I’ll use WebSockets,” or “character-level changes need sub-second propagation.” And know when NOT to: if you can tolerate polling, you avoid both hops — a simplicity highly valued at senior+.

12Common deep dives

The follow-ups interviewers love

Connection failures & reconnection

Networks are unreliable — mobile drops, WiFi cuts out, servers restart. The challenge is detecting disconnects fast and resuming without data loss. WebSocket breaks don’t always signal cleanly: a client may think it’s connected while the server already cleaned up (“zombie” connections). Use heartbeats to detect them. For recovery, track what each client has received (per-user message queue or sequence numbers) and replay the gap on reconnect — Redis streams are popular here.

The celebrity problem (millions of followers)

When a celebrity posts, millions need the update at once; naive fan-out crashes the system. The fix is strategic caching + hierarchical distribution: cache the update once and distribute through layers — regional servers pull it and push to their local clients — instead of writing to millions of individual feeds.

Message ordering across servers

Two messages sent milliseconds apart can arrive out of order across different paths/servers. Vector clocks or logical timestamps establish ordering relationships. For strict ordering, funnel related messages through a single server or partition — trading some scalability for a simple total order.

For product-style interviews

Prefer the single server/partition for ordering. Vector clocks matter for deep-infra questions, not for “Design an Online Auction.” If all messages hit one host, stamping a correct timestamp and establishing total order is straightforward.

13Active recall

Reconstruct it without the notes
What are the “two hops,” and why solve them separately?

Hop 1 is server→client (a protocol/networking choice: polling, long polling, SSE, WebSockets, WebRTC). Hop 2 is source→server — routing an update to the specific server the target client is connected to (pull-polling, consistent hashing, or Pub/Sub). They’re a natural partition with different trade-offs, so you pick each independently, then combine.

Why is naive polling so wasteful — and when is it still the right call?

Each request can pay DNS + a 3-way TCP handshake + a 4-way teardown, and ~90% of the time the answer is “nothing new,” so you burn overhead for nothing (×millions of users). Yet it’s still the right default when you don’t need sub-second updates: it’s simple, stateless, needs no special infra, and is fast to explain. Use HTTP keep-alive longer than the interval to cut setup cost.

Why does long polling add latency for the second of two close updates?

Each request yields a single response, so after update 1 returns, the client must open a new request before update 2 can be sent. At 100ms latency, update 1 arrives ~100ms after it happened, but update 2 can be ~290ms late (90ms to finish response 1 + 100ms for the next request + 100ms for its response). That’s why long polling is unsuitable for high-frequency updates.

How does SSE fix long polling’s frequency problem, and what’s its main limitation?

SSE uses Transfer-Encoding: chunked to send many events down one open HTTP response, so there’s no client call-back gap between updates. Limits: it’s one-way (server→client) and some proxies/load balancers buffer streaming responses, silently breaking it. Reconnection uses a “last event ID” to replay missed events.

When do you choose WebSockets over SSE, and what infra do they demand?

Choose WebSockets for frequent bidirectional communication; otherwise prefer SSE (a common pattern is SSE for updates + HTTP POST for writes). WebSockets need an L4 load balancer (bare TCP), should terminate into a dedicated WebSocket service early to keep other services stateless, and require handling long-lived connections, deployments/reconnection, and least-connections balancing.

Consistent hashing vs Pub/Sub for hop 2 — how do you decide?

Ask how stateful your services are. Heavy per-connection state (e.g. a Google Doc’s editing state) → consistent hashing, which pins each user to one server so state lives in one place and scaling moves only a fraction of connections. Small, stateless message passing → Pub/Sub (the default): endpoint servers are dumb proxies, state lives in the Pub/Sub service, and any user can connect to any server.

What is the thundering-herd risk in consistent hashing, and how is it mitigated?

If you drop all users off a server at once (or kill a server mid-transition), you trigger mass reconnection — and new connections are resource-heavy, causing more churn. Mitigate by formally signaling the scaling event, keeping both old and new assignments live during the transition (dual-sending messages), and draining connections slowly with back-pressure so new servers absorb them gradually.

Redis vs Kafka for Pub/Sub — what decides it?

Durability. Redis Pub/Sub is in-memory, no durability — ideal fire-and-forget (e.g. a stock ticker where only the latest value matters). Kafka provides replay logs, so when every update must eventually reach users you can store messages in the topic and replay what a reconnecting client missed.

Why can a huge chat room push you from consistent hashing to Pub/Sub?

With consistent hashing, everyone in a room maps to the same server, so a room with millions of users overloads a single server. Pub/Sub spreads those connections across interchangeable endpoint servers and broadcasts via topics, avoiding the single-server hotspot.

14Quick revision

Rapid refresh

The frame

Two hops: Hop 1 = server→client (protocol). Hop 2 = source→server (routing). Solve separately. Start with polling; escalate only for genuine sub-second needs. WebSockets are a last resort.

Hop 1 protocols at a glance

ProtocolDirectionBest forMain cost
Simple pollingclient pullsNot latency-sensitive; short windowsLatency; wasted requests
Long pollingclient pulls (held)Infrequent near-real-time (e.g. payment status)Bad for high frequency; hidden state
SSEserver → clientOne-way push, high frequency (AI token streams)One-way; proxy buffering breaks it
WebSocketsbidirectionalFrequent two-way (chat, collab edit)Stateful; L4 LB; deploy/reconnect complexity
WebRTCpeer ↔ peerAudio/video, gamingSignaling + STUN/TURN; complex

Hop 2 patterns

  • Pull polling — server queries a DB; sidesteps routing; high latency; watch read TPS (1M clients / 10s = 100k TPS).
  • Consistent hashing — pin user→server via a ring (ZooKeeper + hash fn). Best when connection state is heavy. Deep dives: orchestration, dual-send during transitions, drain to avoid thundering herd, state lost on failure.
  • Pub/Sub — default. Endpoint servers are dumb proxies; state in the service. Redis (no durability, fire-and-forget) vs Kafka (replay). SPOF/bottleneck → Redis cluster + least-connections LB.

Decision cues

  • Hop 1: latency-sensitive? → no: polling · one-way: SSE · bidirectional: WebSockets · audio/video: WebRTC.
  • Hop 2: heavy per-connection state? → consistent hashing · else Pub/Sub.
  • Infra: WebSockets → L4 LB + terminate early; SSE/long-poll → don’t let proxies buffer or hang up; endpoint servers → least-connections.

Deep-dive triggers

  • Reconnection: heartbeats for zombie connections; sequence numbers / Redis streams to replay missed messages.
  • Celebrity fan-out: cache once + hierarchical/regional distribution.
  • Ordering: single server/partition for a total order (vector clocks only for deep infra).