Realtime Communication
Push updates to users the moment they happen: polling, long polling, server-sent events and WebSockets, and how open connections reshape the design.
On this page
A chat app has 1 million people with the app open. To see new messages, each phone asks the server "anything new?" every 5 seconds. That is 1,000,000 ÷ 5 = 200,000 requests a second. About 2,000 new messages arrive a second in total, so at most 2,000 of those 200,000 answers carry anything: 99% of the work returns "no". And a message still waits 2.5 seconds on average before anyone sees it.
The fix is to turn the direction around: keep a connection open and let the server speak first. That is cheap for the network and expensive for your architecture, because now your servers remember who is connected to them.
The idea
HTTP is built for the client to ask and the server to answer. Realtime features need the server to send when something happens. There are four ways to get there, from "ask often" to "hold a two-way pipe open":
- Short polling: the client asks on a timer.
- Long polling: the client asks, and the server holds the request open until there is news or about 30 seconds pass. Then the client asks again at once. Messages that arrive between two polls must be fetched with a cursor ("everything after message 9001"), or they are missed.
- Server-sent events (SSE): one HTTP response that never ends; the server writes events into it as they happen. One direction only, server to client, text only. Browsers reconnect automatically and send the last event id they saw, so a server that tags events with ids can resume.
- WebSocket: starts as an HTTP request asking to upgrade, then becomes a two-way channel of messages over the same TCP connection. Nothing is built in for reconnecting or replaying missed messages; you write that.
| Option | Good at | Bad at | Pick it when |
|---|---|---|---|
| Short polling | Plain HTTP requests; works with every cache, proxy and server. | Mostly empty responses, and updates arrive up to one interval late. | Updates are rare and a delay is fine: a report finishing, every 30 seconds. |
| Long polling | Near-instant delivery over plain HTTP, through any proxy. | A new request after every message, and gaps between polls need a cursor. | A fallback where WebSockets and SSE are blocked. |
| Server-sent events | Simple server push over HTTP, with reconnect and resume built into browsers. | Server to client only. On HTTP/1.1, browsers cap open connections per host at a handful. | One-way feeds: notifications, live scores, streaming text. |
| WebSocket Usual default | Low-overhead messages both ways over one connection. | You build heartbeats, reconnects and replay yourself; some networks block it. | Two-way and frequent: chat, multiplayer, collaborative editing. |
For chat, pick WebSockets: both sides talk often. Pick SSE when the client only listens. Keep short polling for things that change every few minutes; a million open connections for one update an hour is waste too.
How open connections change the design
With request and response, any server can answer any request. With a WebSocket, Ben's connection lives on one particular server for hours. When Ana sends Ben a message, it lands on Ana's server, and something must get it to Ben's. That one fact reshapes the system:
- Connection servers become their own tier. They hold sockets and pass messages through, nothing else, so business logic can be deployed without dropping a million connections.
- Pub/sub routes messages between servers. When Ben connects, his server subscribes to a channel
user:ben. Anyone with a message for Ben publishes to that channel. The alternative is a registry ("Ben is on server 7") in a shared store. Pub/sub keeps that registry for you: a dead server's subscriptions vanish with it. - The database stays the source of truth. Basic pub/sub delivers only to whoever is subscribed at that moment, at most once. Store the message first, so a client that was offline can catch up.
1Ana's socket is on server 1 and Ben's on server 2. When Ben connected, server 2 subscribed to
user:ben.- last id Ben saw
- 9000
2Ana sends "lunch?" over her open socket: no new connection, no handshake.
- last id Ben saw
- 9000
3Server 1 hands it to the chat service, which stores it as message 9001 before anything else. If delivery fails later, the message still exists.
- last id Ben saw
- 9000
4The chat service publishes 9001 to
user:ben. It does not need to know which server Ben is on.- last id Ben saw
- 9001
5Server 2 is the only subscriber to that channel, so it receives 9001 and pushes it down Ben's socket.
- last id Ben saw
- 9001
6Server 2 crashes. Ben's socket drops, and nobody is subscribed to
user:benany more. Ana's next message, 9002, is stored, but the publish reaches no one.- last id Ben saw
- 9001
7Ben's app waits a random 0 to 30 seconds, then reconnects through the load balancer and lands on server 1. Server 1 subscribes to
user:benbefore the app asks for history, so nothing can slip between the two.- last id Ben saw
- 9002
8The app sends "give me everything after 9001". The store returns 9002, and Ben misses nothing. The app drops any id it already has. The store, not pub/sub, made delivery reliable.
The random wait in step 7 matters. Server 2 may have held 50,000 sockets. If all reconnect in the same second, they hit the rest of the fleet like a small attack. Spread over 30 seconds, it is 50,000 ÷ 30 ≈ 1,700 a second.
Long-lived connections bring two chores. Heartbeats: send a small ping every 30 seconds or so, because a phone that loses signal does not close its socket, and idle connections get cut by proxies. Deploys: drain a server by telling clients to reconnect elsewhere, a slice at a time, instead of killing it with everyone attached.
Numbers worth remembering
- Polling load = users ÷ interval in seconds.
- An idle connection costs memory, not CPU. Assume a scenario number such as 50,000 per server and say you would load test it.
- Heartbeat load = connections ÷ heartbeat interval: 2 million connections pinging every 30 seconds is about 67,000 small messages a second.
How many connection servers do you need for the peak number of open connections?
- 10 million daily active users
- At peak, 20% of them have the app open
- Assume 50,000 connections per server
Show the working
- Open connections at peak: 10 million × 0.2 = 2 million
- Servers: 2,000,000 ÷ 50,000 = 40
- Add spare capacity so one dying server's clients have somewhere to go
Estimate 40 (anything from 15 to 120 is a good answer)
In the interview
Bring this up as soon as a requirement says "instantly", "live" or "as it happens": chat, notifications, a driver's position, a shared document. Pick the transport, then spend your time on the hard part, which is routing and recovery. Phrases that show judgement: "Users both send and receive, so WebSockets." "Connection servers are stateful, so I keep them thin." "Pub/sub is fire-and-forget, so the store is the truth and clients resync by last message id."
How does a message reach the recipient?
The server sends it over the recipient's WebSocket.
How does a message reach the recipient?
The sender's connection server passes it to the chat service, which stores it and publishes to the recipient's channel. Whichever connection server holds the recipient is subscribed and pushes it down. If they're offline, nobody's subscribed, and they fetch it from the store by last id when they reconnect.
Follow-ups to expect:
- "A user has a phone and a laptop." Two connections, possibly on two servers, both subscribed to the same user channel, so both get the message.
- "How do you balance WebSocket traffic?" Least connections, long idle timeouts on the balancer, and draining on deploy.
- "Are messages in order?" Within one conversation, yes: the store gives each message the next id in that conversation and clients sort by it. Global order is not needed.
- "What if a publish is lost while Ben is online?" His app sees a gap, 9003 after 9001, and fetches 9002 from the store. A lost last message leaves no gap, so apps also sync when opened.
Common mistakes
300,000 users keep a dashboard open that polls every 10 seconds. How many requests a second is that?
A sports site shows live scores in the browser. Readers never send anything back. Which transport fits best?
A connection server holding 60,000 sockets dies. Clients wait a random 0 to 30 seconds before reconnecting. About how many reconnects a second hit the other servers?
What to practise next
Open connections invite abuse and floods, which is where Rate Limiting comes in. To put all of this into a full design, work through Design a Chat App.