The Runtime Theory
mediumApplicationDSA#system-design#data-structures#realtime

Design a leaderboard for a million players

Checks whether you can pick the data structure that makes O(log n) writes and reads trivial, do the memory and ops math, and explain what changes when one node stops being enough.

The Runtime Theory Team2 min readasked at meta · netflix · google · amazon

A leaderboard is a sorted data structure problem wearing a game's clothes. The interview tests whether you reach for the right structure, do the per-op math, and know the moment it stops fitting on one node.

Requirements and capacity

1M players; score updates every few seconds during a match → ~50k updates/s sustained at peak; reads: top-100 page ~5k QPS, "my rank" ~2k QPS; p99 read <50ms; persistence required across restarts. Capacity planning first (capacity-planning-the-math): updates at 50k/s, reads at 7k/s — the ratio is write-heavy, which is backwards for most systems and tells you where the money goes.

The core structure: a sorted set

Redis sorted set: ZADD lb:season {score} {player_id} is O(log n) — at 1M members that's ~20 comparisons per update. A single Redis instance does 100k–1M ops/s, so 50k updates/s and 7k reads/s fit on one node — that is the first answer, and it is the right one. ZREVRANGE 0 99 serves the top-100 in O(log n + 100); ZREVRANK {player_id} gives exact rank in O(log n). Memory: 1M members × (~10B id + 8B score + ~60–100B zset overhead) ≈ 80–120MB — trivial. State these numbers; the capacity math is the point.

Write coalescing

A player updating 10x/s only needs their latest score written: coalesce in the game service (one update per player per second in flight), cutting effective write rate ~5x. Small change, big effect on the node's headroom — interviewers listen for it.

Durability

The sorted set is the live system of record; snapshot to a DB hourly (or journal the updates) so a restart rebuilds in minutes, not hours. The zset is rebuilt by replay — the DB table player_scores(player_id, score, updated_at) plus the journal is the recovery story. State the RPO: the last hour of score changes is the loss window.

Sharding (when one node stops being enough)

Split by score band (e.g., four shards by percentile) or by player hash. A global top-100 then requires each shard to return its top-k and the read API to k-way merge — the merge is the price of sharding. Exact global rank gets expensive: shards must agree on tie-breaking to merge ranks correctly. Alternatives: approximate rank (score bucket + offset) or exact rank within tier. Say the threshold where you shard (single-node ops ceiling minus headroom) rather than the shard count.

Tie-breaking

Score collisions are guaranteed at scale. Encode score × 1e6 + submission_sequence, or compare (score, earlier-timestamp) — decide once, enforce in the structure, and never let the merge path reinterpret it.

Tradeoffs and bottlenecks

Exact vs approximate rank past one node; single-node simplicity vs merge complexity; freshness of the top-100 page (cache it with a 2–5s TTL — nobody needs 1s freshness on a ranking page); snapshot write amplification; and the single hot zset key, which is fine at 1M players and the first thing you fix at 10M.

This answer walks

Follow-ups they'll push on

  1. 01Scores update 10 times per second during a match. Does the design change, and where does the write coalescing go?
  2. 02The leaderboard is global and one node can't take the updates. How do you shard it and still serve a correct top-100?
  3. 03How are ties broken — and where is that decision enforced?
  4. 04Redis restarts mid-season. Walk through what the players see and how fast you recover.

More interviews in this topic

One dispatch a week

The trace behind each question, the tradeoff that explains it, and one technical dispatch per week — no noise.

One technical dispatch per week. No noise.