Patterns · Real-time updates

Patterns

Real-time updates

How a client learns that something changed: polling, SSE, WebSockets, and the fan-out behind them.

2 min read system-design

“Real-time” means the user sees a change without refreshing, within a bound you state (a second for chat, a few seconds for a dashboard). The transport is the easy part. The hard part is getting the event to the right connection and not losing the ones that happen during a reconnect.

Choose a transport

Polling. The client asks on an interval. Simplest. Wasteful if almost nothing changes, and perfectly fine if you say “every two seconds is acceptable” and the numbers work. Conditional GETs and long polling (the server holds the request until there is news or a timeout) are the grown-up versions.

SSE. One HTTP response that stays open and carries events. Works with ordinary infrastructure. The client reconnects with Last-Event-ID. Use it when the server pushes and the client sends ordinary HTTP for its actions.

WebSockets. Both directions, one connection. Use it for chat, cursors, and games. You now operate a long-lived connection fleet: heartbeats so dead clients go away, a load balancer that does not idle-timeout the socket, and a plan for deploys.

The fan-out

Do not store “which socket” inside the database row. The API writes the durable event (the message row), then publishes a thin notification (conversation_id, sequence) on Redis pub/sub, NATS, or a Kafka topic. Each socket server subscribes to the conversations its local clients care about and writes to those sockets. Any API instance can publish. Any socket instance can deliver.

Presence (“is this user online?”) is a lease in Redis, refreshed by the heartbeat, not a boolean in Postgres you update on every packet.

Correctness

Give events a per-conversation sequence. The client applies them in order and asks for the gap if it sees a skip. On reconnect it sends the last applied sequence. If the gap is still in the hot log, replay it. If not, the client refetches the resource. That is the whole reliability story, and it is the same story on mobile networks that drop constantly.

flowchart LR
  API[API write] --> DB[(Messages)]
  API --> Bus[Pub/sub]
  Bus --> S1[Socket node]
  Bus --> S2[Socket node]
  S1 --> C1[Client]
  S2 --> C2[Client]

Further reading

Check yourself

Answer the questions. Multiple choice is scored on this device only.

1. Which design lets any API server notify a user connected to a different socket server?

2. A client reconnects. What should it send?

3. When is polling the right answer?