/projects/dietbox-realtime

All projects

Dietbox Realtime

Live updates as a service of its own, so they ship on their own clock.

Senior Software Engineer · 2022

NodeExpressSocket.IOApplication InsightsAzure App ServiceAzure DevOps
WebsitePrivate

Overview

Thirty-four commits over two months in 2022, for a service that has outlived both: a socket server that holds every open connection, joins each client to a room named for its user id, and exposes one endpoint the rest of the platform posts to when something needs pushing out. It sits outside the product because a long-lived connection and a request are not the same kind of traffic.

What I did

I built this one effectively alone: thirty-three of the thirty-four commits, from the handshake to the load-test harness that proved it held up.

  • The socket server itself: the shared-secret handshake, room assignment by user id, and an immediate disconnect for a client that ends up joined to no room.
  • The notify endpoint the rest of the platform posts to, and the info and health endpoints used to watch the service itself.
  • The handler-loading convention: an event handler is a file, picked up automatically from a directory.
  • The load-test harness, built to deliberately hold a share of clients on long-polling instead of letting all of them upgrade.

The problem

The monolith deployed once a night, and anything sharing its pipeline shared its cadence — a realtime channel that can only change at three in the morning is a realtime channel nobody changes. Separately, open connections and request traffic do not want the same instance count: one scales with how many people are online, the other with how many requests arrive.

Architecture

The platform posts a room, an event name and a payload to the notify endpoint; the server resolves who is in that room right now and pushes the event straight to them.

  1. PlatformAnother service in the platform posts a room, an event name and a payload to the notify endpoint.
  2. Room resolvedThe server looks up which connections are actually joined to that room right now.
  3. Fan-outThe event is pushed to every client currently joined to the room.
  4. BrowserThe client receives the event and updates without a refresh.

What it does

  • The notify endpoint the rest of the platform posts to when something needs pushing out.
  • An info endpoint reporting the live connection count, for monitoring.
  • A health endpoint reporting its own latency.
  • A shared-secret handshake that disconnects a client immediately if it ends up joined to no room.

Engineering decisions

  • Realtime as its own deployable

    Two reasons, both real: open connections and request traffic scale on different axes, and the product deployed once a night — a channel that can only change at three in the morning is one nobody changes. Splitting it into its own service let each axis scale on its own terms and let this one ship on its own clock.

  • A room per user id

    Addressing is by identity, not by connection, so the platform can push to a person without knowing how many tabs, devices or reconnects that person currently has open.

  • Handlers auto-loaded from a directory

    Adding an event is adding a file — there is no registry to remember to update, and no handler that exists in the code but was never wired in.

  • A load test that keeps clients on long-polling

    Not every client upgrades to a websocket. A load test where all of them do measures a population that does not exist, so the harness deliberately holds a share of clients on HTTP long-polling instead.