Real-Time Distributed Chat System
Built to figure out what breaks when WebSocket traffic has to live across multiple servers. Go backend, React frontend, Redis pub/sub for cross-instance messaging, a custom load balancer, and the whole thing runs in containers.
A chat system built to study what actually breaks when WebSocket traffic has to live across multiple servers. Goroutines handle connections, Redis pub/sub fans out messages between nodes, a small custom load balancer sits in front. The interesting part is not the chat; it's what stops working when you go from one node to two.
Context
The core question behind this project: what actually breaks when you try to scale WebSocket connections across multiple server instances? Single-server chat is simple: an in-memory map of user ID to connection and you're done. Multi-server chat requires real coordination work, because a message arriving on one node needs to reach users connected to any other node.
Architecture
The backend is Go, chosen for its concurrency model; goroutines handle individual WebSocket connections cleanly without the callback soup of a single-threaded event loop.
Redis pub/sub handles message fanout across server instances. When a client sends a message, the receiving node publishes it on a channel; every node is subscribed and forwards the message down any local connections that should receive it. This keeps per-node state small: you don't need to know the full membership of a channel, only which of your local connections care.
A custom load balancer sits in front and distributes new connections across nodes. The React frontend is intentionally minimal; all the coordination work is in the backend.
What I took from it
The distributed version is a different program, not a bigger version of the same program. State assumptions that work on one node, like "I can look up that user, they're in my map", stop being true. Pub/sub is cheap as a coordination primitive once you accept that no node has the full picture.