Connection affinity and connection registries
coreintermediateA WebSocket is a long-lived, stateful TCP connection — once a client's handshake completes, that specific connection is held open in the memory of exactly one server process, on exactly one machine. This is connection affinity: unlike a stateless HTTP request, which any server behind a load balancer can answer, a WebSocket message can only be pushed to a client through the one process that is holding its open socket. A connection registry is the piece of shared infrastructure — usually Redis or a database — that records which server instance currently holds which user's connection, so any other part of the system can look up where to send a message.
Think of it as
Connection affinity is like a phone call versus a letter. A letter (HTTP request) can be handed to any postal worker who happens to be free — none of them need to remember you. A phone call (WebSocket) is answered by one specific operator, and it stays connected to that operator for the whole conversation; if someone else in the building wants to relay a message to you, they cannot just pick up any phone — they have to find out which operator's line you are on. The connection registry is the building's switchboard log that records "caller 482 is on operator 7's line right now."
What we're doing: Route a server-initiated push to a user whose WebSocket lives on a different instance.
- 2
- The instance that actually accepted the handshake is the only one that can write to this specific socket — this is connection affinity in effect.
- 4
- The registry lookup is what makes step 5 possible at all — without it, server-3 has no way to know user 482 is even connected, let alone to which instance.
- 10
- server-3 never touches the socket itself; it hands the message to a relay mechanism (pub/sub or a broker) that reaches the correct instance.
Why this works: This is the concrete two-hop pattern every WebSocket fan-out system uses: a registry to find the right instance, then a relay to actually deliver the message through it — the registry alone cannot deliver anything, and the relay alone has no idea who to deliver to.
Letting a crashed instance leave a stale registry entry behind
Wrong
Better
What you see: Messages meant for a user are silently dropped, or errors accumulate trying to reach a dead instance, minutes or hours after that instance actually crashed — the registry still confidently reports a location that no longer holds any connection.
Why: A registry entry written only on connect and removed only on graceful disconnect has no way to reflect an ungraceful failure (crash, network partition, OOM kill) — a short TTL refreshed by the heartbeat mechanism bounds how long a stale entry can mislead the rest of the system.
- Client → server-7: WebSocket handshake
- server-7 → Registry: user:482 → server-7
- server-3 → Registry: lookup user:482
- Registry → server-3: server-7
- server-3 → server-7: relay via pub/sub
- server-7 → Client: write to live socket
Stateless HTTP vs stateful WebSocket routing
Remember: A WebSocket is pinned to one server process (connection affinity) — a registry (typically Redis, with a short TTL refreshed by heartbeats) is the only way other instances learn where a given user's connection actually lives.
See also: pubsub fanout across instances · connection lifecycle · redis use cases

