runtime/rants

Project

realtime-odds-platform

active

Real-time odds delivery: a sharded WebSocket hub that drops slow consumers, with RabbitMQ queues partitioned by event to keep per-event order (in progress).

go · rabbitmq · websockets

What it is

A real-time odds platform: it ingests event updates from external providers and pushes them to subscribed clients over WebSocket. It comes from a system design problem I was given in a sportsbook interview.

The interesting constraint is ordering: updates for one event must reach the client in order, while updates for different events run in parallel. The WebSocket gateway is working; the ingestion pipeline is in progress.

Architecture

  • Ingestion. Providers push updates into N RabbitMQ queues routed by hash(eventId) % N, with one worker per queue. Each worker writes the new state to Redis and publishes the fragment to Redis Pub/Sub.
  • Gateway. A client opens /ws and sends subscribe(eventId); one connection can follow many events. The hub is sharded by hash(eventId), and each shard maps an event id to its subscribed clients.
  • Connections. Each connection runs a readPump for client commands and a writePump, the only goroutine that ever writes to the socket.

The repository is a monorepo with two independent services, each with its own Go module and its own hexagon (ports and adapters).

What I learned

The trade-offs from the README’s design notes:

  • Hash-partitioned queues give parallelism between events and serialisation within one. Same idea as Kafka partitions, applied to RabbitMQ.
  • Sharding the hub means updates for unrelated events never contend for the same lock.
  • One write pump per connection, because Gorilla WebSocket connections do not support concurrent writers.
  • Non-blocking send. Dropping a slow consumer is deliberate: availability of the fan-out over delivery to one client.
  • Redis holds state as well as transporting it, because Pub/Sub has no replay and a client connecting between updates would otherwise see nothing.

What’s next

Finishing the ingestion pipeline, which the README lists as in progress.