/projects/dietbox-realtime

← All projects

Dietbox Realtime

Chat between a nutritionist and her patient, and anything else that has to arrive now.

Senior Software Engineer · 2022

NodeExpressSocket.IOApplication InsightsAzure App ServiceAzure DevOps
WebsitePrivate

Overview

This exists so two people can talk inside the product. A nutritionist and her patient each hold an open connection, and a message sent from one lands on the other’s screen without either of them reloading anything. The same channel carries the platform’s own notifications — anything that has to reach someone now rather than at their next page load. Thirty-four commits over two months in 2022, for a service that outlived both.

What I did

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

  • The chat relay: a message emitted by one client is pushed straight into the recipient’s room, so it reaches an open screen rather than waiting for a reload.
  • The socket server underneath both: the shared-secret handshake, room assignment by user id, the notify endpoint the platform posts to, and the info and health endpoints used to watch it.
  • 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

A nutritionist and her patient had no way to talk inside the product, and anything the platform needed to tell someone waited until that person reloaded the page. Putting the open connections inside the monolith was not an option: it deployed once a night, so anything sharing that pipeline could only be changed then. Open connections also scale with how many people are online, while requests scale with how many arrive.

Architecture

Two things arrive the same way: a chat message emitted by one of the two people talking, or a push from another service in the platform. Either resolves to a room, and whoever is in that room right now gets it.

  1. OriginEither one of the two people talking emits a chat message, or another 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

  • Chat between a nutritionist and her patient, carried over the connection both of them already hold open.
  • Notifications the platform pushes to a person, landing on whatever screen they already have open.
  • An info endpoint reporting the live connection count, and 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, which set the pace for anything inside it. Splitting it into its own service let each axis scale on its own terms and let this one ship on its own schedule.

  • 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.