Capabilities/Real-Time & Communication

WebSocket Infrastructure

Persistent bidirectional connections at scale for real-time applications

WebSocketreal-timebidirectionalscalingpub/sub

Learning lens

Start with judgment, not a universal prescription.

This page is a learning model for understanding the capability. It does not claim exact production internals, exact costs, or an infinite-scale architecture.

Evidence

Conceptual pattern

0->1 default

Start with the simplest delivery path that meets latency needs before introducing durable fanout or global state.

Cost drivers

open connectionsfanout workpresence updatescross-region traffic

Watch for breakpoints

  • -connection count grows faster than app servers
  • -message fanout dominates request work
  • -presence state becomes stale or expensive

Learn first

Concepts behind this architecture

The Problem

Applications like chat, multiplayer games, collaborative editors, and live trading platforms require low-latency bidirectional communication between client and server. HTTP request-response adds too much overhead per interaction. WebSockets upgrade an HTTP connection to a persistent, full-duplex TCP channel — but scaling millions of concurrent WebSocket connections across a fleet of servers introduces challenges around connection management, message routing, state synchronization, and fault tolerance.

Architectural Approaches

Sticky Sessions with In-Process State

Route each client to a specific server instance using sticky sessions (IP hash, cookie, or connection ID). Each server holds connection state in memory and routes messages locally. Simple to build but limited in scale and resilience.

Pros

  • +Simplest implementation — no external state store needed
  • +Low latency for messages between users on the same server
  • +Easy to reason about — each server is self-contained

Cons

  • −Server failure drops all connections on that instance
  • −Cross-server messaging requires an additional routing layer
  • −Uneven load distribution as sticky sessions prevent rebalancing
  • −Horizontal scaling is limited by single-server capacity

Pub/Sub Backbone with Stateless Gateways

WebSocket gateway servers are stateless beyond holding connections. All message routing goes through a central pub/sub system (Redis Pub/Sub, NATS, Kafka). When user A sends a message to user B, the gateway publishes to the bus, and the gateway holding B’s connection delivers it.

Pros

  • +Gateways are interchangeable — easy horizontal scaling
  • +Server failure only requires clients to reconnect to any instance
  • +Clean separation between connection management and business logic
  • +Supports broadcast, multicast, and unicast patterns

Cons

  • −Added latency from pub/sub hop (typically 1-5ms)
  • −Pub/sub becomes a critical dependency — must be highly available
  • −Fan-out to all gateways can be wasteful for unicast messages
  • −Message ordering across partitions requires careful design

Distributed Actor Model

Each connection and each chat room/channel is modeled as an actor (Erlang/Elixir processes, Akka actors, Cloudflare Durable Objects). Actors communicate via message passing and can be distributed across nodes. The runtime handles location transparency and fault recovery.

Pros

  • +Natural modeling of concurrent connections and rooms
  • +Built-in fault isolation — one actor crash doesn't affect others
  • +Location-transparent messaging simplifies cross-node routing
  • +Supervision trees enable automatic recovery

Cons

  • −Requires a runtime that supports the actor model
  • −Debugging distributed actors can be complex
  • −Mailbox overflow under load requires backpressure design
  • −Less common skillset in most engineering teams

Architecture

Loading diagram...
WebSocket Infrastructure — High-level architecture

Key Components

WebSocket Gateway

Accepts WebSocket upgrades, manages connection lifecycle (open, message, ping/pong, close), authenticates on connect, and routes messages to/from the pub/sub backbone. Each instance handles 50K-500K concurrent connections depending on message throughput.

Connection Registry

Maps user IDs to gateway instances and connection IDs. Stored in Redis with TTL-based expiry. Used for targeted message delivery and presence tracking. Updated on connect/disconnect events.

Pub/Sub Backbone

Routes messages between gateway instances. Redis Pub/Sub for low-latency fan-out, or NATS/Kafka for higher durability. Supports channel-based subscriptions for rooms/topics and user-specific channels for DMs.

Message Service

Handles message persistence, history retrieval, and delivery receipts. Writes to the database and publishes events to the pub/sub bus. Decoupled from the gateway layer for independent scaling.

Auth Middleware

Validates JWT or session tokens during the WebSocket handshake. Rejects unauthorized connections before the upgrade completes. May also handle per-message authorization for sensitive operations.

Health & Heartbeat Manager

Sends periodic WebSocket ping frames to detect dead connections. Cleans up stale entries in the connection registry. Monitors gateway health and triggers graceful draining during deploys.

Data Model

Loading diagram...
WebSocket Infrastructure — Entity relationship diagram

Connection Lifecycle

A WebSocket connection goes through several stages:

  1. HTTP Upgrade — Client sends an HTTP request with Upgrade: websocket header. Server validates auth token (from query param or cookie) and responds with 101 Switching Protocols.
  2. Initialization — Server registers the connection in the registry, subscribes to relevant pub/sub channels, and sends an initial state payload (unread counts, presence data).
  3. Steady State — Bidirectional message exchange. Server sends ping frames every 30s; client responds with pong. Messages are routed through the pub/sub backbone.
  4. Graceful Close — Either side sends a close frame with a status code. Server unsubscribes from channels, removes the registry entry, and notifies presence subscribers.
  5. Abnormal Close — Network failure or crash. Detected via ping timeout (typically 60-90s). Cleanup runs asynchronously, and the client reconnects with exponential backoff.

Reconnection: Clients should implement exponential backoff with jitter (e.g., 1s, 2s, 4s + random 0-1s). On reconnect, send the last received message ID to enable server-side replay of missed messages.

Loading diagram...
Connection Lifecycle

Scaling to Millions of Connections

Per-server limits: A single server with 16GB RAM can hold ~500K idle WebSocket connections (each consuming ~30KB). Active connections with message buffering need more memory. Key OS tunings:

  • Set ulimit -n to 1M+ for file descriptors
  • Tune net.core.somaxconn and TCP buffer sizes
  • Use epoll (Linux) or kqueue (BSD/macOS) for efficient I/O multiplexing

Horizontal scaling strategy:

  1. Add gateway instances behind a Layer 4 load balancer
  2. Shard pub/sub channels to avoid hotspots (e.g., partition by channel ID hash)
  3. Separate read and write paths — message persistence can be async
  4. Use connection draining during deploys — stop accepting new connections, wait for existing ones to close or migrate

Cost optimization: WebSocket connections are long-lived and consume resources even when idle. Consider:

  • Downgrading idle connections to SSE or long-polling
  • Implementing connection quotas per user
  • Using serverless WebSocket services (AWS API Gateway WebSocket, Cloudflare Durable Objects) for variable workloads

Message Ordering and Delivery

Within a connection: TCP guarantees in-order delivery. Messages sent on a single WebSocket connection arrive in order.

Across connections: When a user reconnects to a different gateway, message ordering depends on the pub/sub system. Solutions:

  • Assign sequence numbers per channel. Clients buffer and reorder if needed.
  • Use Kafka partitions keyed by channel ID for strict per-channel ordering.
  • Accept eventual consistency for non-critical updates (typing indicators, presence).

Delivery guarantees:

  • At-most-once: Default WebSocket behavior. Message lost if connection drops mid-delivery.
  • At-least-once: Persist messages before acknowledging to sender. Replay on reconnect using last-message-ID. Clients deduplicate using message IDs.
  • Exactly-once: Very expensive. Requires idempotent message processing and acknowledgment tracking. Rarely needed — at-least-once with client dedup is sufficient for most use cases.

Real-World Examples

Slack

Uses WebSocket connections for real-time messaging with a Gateway Fleet pattern. Falls back to long-polling when WebSocket is unavailable. Messages are persisted in MySQL and routed via an internal message bus.

Discord

Runs millions of concurrent WebSocket connections on Elixir/Erlang gateway nodes. Each guild (server) is managed by a process that handles message fan-out to connected members. Uses consistent hashing for guild-to-node assignment.

Figma

WebSocket connections for real-time collaborative design. Uses a custom CRDT-based sync protocol over WebSocket for conflict-free concurrent editing. Each document session is managed by a dedicated server process.

Binance

Streams real-time market data to millions of traders via WebSocket. Uses a tiered fan-out architecture: internal pub/sub → edge gateways → client connections. Supports per-symbol and aggregate streams.

Architectural Tradeoffs

Layer 4 (TCP) vs Layer 7 (HTTP) load balancing

Pros

  • +L4 is simpler and lower latency for persistent connections
  • +L7 enables smarter routing (auth-aware, header-based)
  • +L4 avoids HTTP parsing overhead on every frame

Cons

  • −L4 cannot inspect WebSocket frames for routing decisions
  • −L7 adds latency but enables features like rate limiting at the LB
  • −Sticky sessions at L4 can cause hotspots

Redis Pub/Sub vs Kafka for message backbone

Pros

  • +Redis Pub/Sub: ultra-low latency (~0.1ms), simple setup
  • +Kafka: durable, replayable, ordered per partition
  • +Redis: better for ephemeral real-time events

Cons

  • −Redis Pub/Sub: fire-and-forget, no durability or replay
  • −Kafka: higher latency (5-50ms), more operational complexity
  • −Kafka: overkill for ephemeral events like typing indicators

Connection-per-user vs multiplexed connections

Pros

  • +One connection per user is simpler to implement and debug
  • +Multiplexing reduces connection count for multi-tab users
  • +SharedWorker or BroadcastChannel can share one WS across tabs

Cons

  • −Multiple connections per user waste server resources
  • −Multiplexing adds client-side complexity
  • −SharedWorker support varies across browsers

Learn WebSocket Infrastructure

Copy a learning context prompt for an AI assistant. It keeps assumptions visible and asks for tradeoffs, cost drivers, failure modes, and what to measure before adding complexity.

Explore More Capabilities