CodePark/ questions / Classic design problems
START PRACTICINGPRACTICE
Backend question · System design & architecture › Classic design problems

Design a chat system supporting one-to-one and group chats, online presence and offline users.

Clients keep a WebSocket open to gateway servers. A chat service saves each message with a sequence number and then routes it to the recipients' gateways. Offline users get a push notification and catch up when they reconnect.

Connections: each client holds a persistent WebSocket (a connection that stays open in both directions). It connects to one of many gateway servers. These gateways are stateful: each one holds its own set of open connections. They sit behind a load balancer that supports long-lived connections. The "least connections" strategy works well here: it sends each new client to the gateway with the fewest open connections. A connection registry, for example in Redis, records which gateway holds each user's connection.

Sending:

  • The client sends a message to its gateway.
  • The gateway forwards it to a chat service.
  • The chat service gives the message a sequence number inside its conversation. This number sets the order.
  • The service saves the message, then sends an acknowledgement to the sender.

For storage, split the data by conversation id and keep messages sorted by sequence number. This matches how the app reads messages.

Delivery: the service looks up the recipients. It routes the message to their gateways through pub/sub (publish/subscribe: a service publishes a message and every listener gets it). In a group, the message goes to each member. This is called fan-out. Very large groups may switch to a model where clients pull new messages instead. Recipients who are offline get a push notification.

Sync: when a client reconnects, it asks for all messages after the last sequence number it saw. This also covers anything it missed.

Other details:

  • Delivery receipts and read receipts are just more events.
  • Presence (who is online) comes from the connection state plus heartbeats. A heartbeat is a small regular "I'm still here" signal. Presence updates are throttled, so they are not sent too often.
  • The client creates each message id. So if the client resends a message, the server sees the same id and doesn't store it twice. This makes resends idempotent.
  • Clients connect to WebSocket gateways, and a registry maps each user to their gateway.
  • The service saves each message first and gives it a sequence number per conversation for ordering.
  • Messages are routed through pub/sub, and group messages fan out to each member.
  • Offline users get a push notification and sync from their last sequence number.
  • Presence comes from heartbeats, and client-created message ids make resends idempotent.