Live Scoring System Low-Level Design: Real-Time Ingestion, Score State, and WebSocket Fan-Out

What Is a Live Scoring System?

A live scoring system ingests real-time score updates from sporting events, games, or competitions, maintains authoritative score state, and fans the updates out to potentially millions of concurrent viewers via WebSocket connections. The design challenges are threefold: ingesting updates reliably from multiple data providers, resolving conflicting or out-of-order events into a consistent state machine, and pushing low-latency updates to a massive number of connected clients without overloading backend services.

Requirements

Functional Requirements

  • Ingest score events (goals, points, penalties, timeouts, game state changes) from official data providers via authenticated feeds.
  • Maintain authoritative score state per game including current score, period/quarter/inning, elapsed time, and game status.
  • Push incremental updates to all subscribed WebSocket clients within 2 seconds of ingestion.
  • Allow clients to request full current state on connection to recover from missed updates.
  • Support tens of thousands of concurrent live games and millions of concurrent viewers.

Non-Functional Requirements

  • End-to-end latency (provider event to client receipt) under 2 seconds at p99.
  • WebSocket connection capacity of 5 million concurrent sessions.
  • Ingest pipeline availability of 99.99% during live event windows.
  • Graceful degradation: clients fall back to 10-second HTTP polling if WebSocket fails.

Data Model

The Game record is the root entity: game ID, sport type, home and away team IDs, scheduled start time, current status (scheduled/live/final), venue, and data-provider game ID. The ScoreState document is stored in Redis and holds the mutable current snapshot: home score, away score, current period, game clock, and an ordered list of the last 50 scoring events. This document is the source of truth for what gets sent on initial WebSocket connection. The ScoringEvent record in PostgreSQL is the immutable append-only log of every event received, including provider timestamp, sequence number, event type, team, player ID, and delta score — used for replay, audit, and highlight generation.

Core Algorithms

Ingest and State Machine

Each sport defines a state machine governing valid transitions (e.g., a basketball game can go from live to halftime to live to final, but not from final to live). The ingest worker applies each incoming event against the state machine. Out-of-order events (detected via sequence number gaps) are held in a per-game reorder buffer (a min-heap sorted by sequence) for up to 500 ms before being applied in order or discarded as stale. Conflicting updates from multiple providers are resolved by preferring the official primary provider and logging the conflict for ops review.

Score State Update

The authoritative ScoreState is maintained in Redis as a JSON document updated with a Lua script to ensure atomicity: the script reads the current sequence number, validates that the incoming event sequence is greater, applies the delta, appends the event to the recent-events list, trims the list to 50 items, and increments a version counter. The version counter is used by the fan-out layer to detect whether clients are behind and need a full resync versus an incremental patch.

WebSocket Fan-Out

After the state update, the ingest worker publishes a scored patch message to a Redis Pub/Sub channel keyed by game ID. A fleet of WebSocket gateway servers each subscribe to all game channels relevant to their connected clients. On receiving a pub/sub message, the gateway iterates its local connection table for that game ID and writes the patch to each WebSocket. This local-delivery model avoids broadcasting to every gateway — each gateway only holds connections for games its clients are watching, reducing unnecessary cross-node traffic.

API Design

The WebSocket API uses a subscribe/unsubscribe protocol. On connection, clients send {"action":"subscribe","game_id":"g123"}. The gateway responds with a full_state message containing the current ScoreState, then streams score_update messages for each subsequent event. Clients can subscribe to multiple games per connection. The REST fallback API exposes GET /v1/games/{id}/score returning the current ScoreState JSON, intended for polling clients and server-side rendering. GET /v1/games/{id}/events?since_seq={n} returns all events after a sequence number, enabling missed-update recovery for WebSocket clients that reconnect.

Scalability and Infrastructure

WebSocket gateways are horizontally scaled behind a Layer-4 load balancer with sticky sessions per game. Each gateway process handles up to 50,000 concurrent WebSocket connections using an event-loop model (Node.js or Go goroutines). The Redis Pub/Sub cluster is sharded by game ID hash. For mega-events (World Cup finals, Super Bowl) with millions of viewers per game, the fan-out is offloaded to a dedicated high-fan-out tier: the gateway publishes to a Kafka topic partitioned by game ID, and a separate layer of edge push servers — colocated with CDN PoPs — consumes the topic and drives the actual WebSocket writes, distributing the fan-out load geographically and reducing origin gateway load by 90%.

See also: Netflix Interview Guide 2026: Streaming Architecture, Recommendation Systems, and Engineering Excellence

See also: Scale AI Interview Guide 2026: Data Infrastructure, RLHF Pipelines, and ML Engineering

See also: Meta Interview Guide 2026: Facebook, Instagram, WhatsApp Engineering

Scroll to Top