← Logs

When the session can't drop

A trading order that fails can be retried. A live tutoring session that drops cannot. The student is mid-explanation, the tutor is mid-sentence, and the reconnect either happens in under a second or the moment is gone. Building the backend for real-time learning taught me that the failure model is the whole design problem: this is not request/response with a faster clock, it is a different shape of system.

The connection is the state

In a request/response service, state lives in the database and each request stands alone. In a live session, the open connection is state. Thousands of students across the region hold a socket open at once, each expecting to send and receive within the window of human attention. The backend’s job stops being “answer this request” and becomes “keep this conversation alive,” which is a much less forgiving contract.

Fan-out through a gateway and a bus

The sessions run through a WebSocket gateway backed by Redis pub/sub. The gateway owns the connections; the bus owns the routing. When a message needs to reach the right participants, it goes through pub/sub rather than being pinned to whichever gateway instance happens to hold the socket. That decoupling is what makes the system scale sideways: add gateway instances as concurrency grows, and no instance needs to know about the others’ connections.

Keep the socket path light

The same discipline that keeps a trading hot path fast applies here, for the same reason. The socket path carries only what the live moment needs. The heavier work, assessment scoring, tutoring records, anything the student is not waiting on this instant, runs as its own service consuming off the side. Splitting the original monolith into focused services was what let those pieces scale and deploy independently, so a slow assessment write can never stall a live session.

Right store for the shape of the data

Not all of this data wants the same home. Structured, relational records, enrollments, schedules, the things with hard invariants, live in PostgreSQL. The flexible, document-shaped data, assessment content and results whose structure varies, lives in MongoDB. Polyglot persistence is easy to overdo, but here the two data shapes are genuinely different, and forcing both into one engine would have meant fighting the store instead of using it.

The principle

Real-time is not regular backend work with a lower latency budget. It is a system whose correctness is measured in whether a human stayed connected. You design for that by deciding what the live moment truly needs, keeping that path clear, and letting everything else catch up behind it.