</> MAANG.io
system design · 301

Advanced

Master system design interviews with scalable architecture patterns, distributed systems, and real-world design challenges.

0/480 solved 0% complete

Section 5: Fifty-Three Bits and One Source of Truth - Data Modeling

Executive Summary: This section turns the API from Section 4 into stores, keys and records. Redis holds one sorted set per board and season (lb:weekly:2026-W44, lb:all-time), with the playerId as the member and a composite score as the score. That composite packs the run's score and a reversed timestamp into 53 bits, so ties resolve to the earlier achiever and every value stays exact in Redis's double. Behind Redis sit the Kafka run-results log (the truth), the player_best table (a conditional max), frozen season_standings for rewards, and a profile cache for display names. We compute Kai's composite to the last digit, settle ember_fox versus quietstorm, and answer the interviewer's favorite pushback: "Why not just Postgres with an index on score?"

The interviewer slides the marker back across the table: "You've got an API. Now tell me what's actually stored, and where."

Many candidates draw one box labeled "Redis" and move on. But a leaderboard has four jobs: remember every run, know each player's best, answer "what rank is 1,284,500?" in microseconds, and still hold last week's top 10,000 after the board moves on. So you say it out loud: four jobs, four stores, and exactly one of them is the source of truth.

Data model: the Score Service appends each run to Kafka run-results (the truth) and updates the weekly and all-time Redis rank indexes; Kafka feeds player_best and the run history lake, the weekly board freezes into season_standings, and the Read API reads replicas plus the profile cache

The Rank Index: One Sorted Set per Board and Season

The Redis side is deliberately boring. Each board is a standalone primary with 2 replicas, not Redis Cluster, and its two side keys live on the same node:

Key Type Member → score Size Lifetime
lb:weekly:2026-W44 sorted set playerId → composite 80M × 100 B = 8 GB Created by the week's first run; frozen (takes no more runs) after the 5-minute grace; kept resident one more week (16 GB with the previous week), then expired
lb:weekly:2026-W44:pending sorted set provisional playerId → composite Small Mirrors its board
lb:weekly:2026-W44:shadow sorted set banned playerId → composite Small Mirrors its board
lb:all-time sorted set playerId → composite 150M × 100 B = 15 GB Forever
lb:all-time:pending sorted set provisional playerId → composite Small Forever
lb:all-time:shadow sorted set banned playerId → composite Small Forever

The interviewer will probe three things in that table.

The season lives in the key name. There is no reset operation. On Monday 00:00 UTC, runs whose endedAt falls in the new week simply start writing to lb:weekly:2026-W45. The old key is frozen, snapshotted and later expired, never deleted at midnight: a DEL of 80 million members would block the board's single main thread at the busiest moment of the week and destroy the evidence rewards need. We also reject true sliding windows: a weekly board is a calendar object, and players understand "this week." If product ever asks for a rolling "last 7 days" board, that's the flip trigger: a live lb:rolling7 key rebuilt daily from durable per-day bests, which Section 11 builds.

The member is the playerId, 48213377, not the handle kai_runs. Three reasons. Handles change, and a renamed member would orphan the old entry. A short numeric id is what the ~100 B per-member estimate from Section 3 assumes. And the friends board needs it: the Read API calls ZMSCORE lb:weekly:2026-W44 with the 41 ids of Kai and Kai's 40 friends in one round trip, sorts the results, and finds Kai 3rd of 41. A per-user friends sorted set would multiply every improvement by 40 writes; we'd reconsider only for clan-sized groups of ~5,000. Names and avatars come from the profile cache, which hydrates each API instance's 100-id top-100 list once a second.

Shadow bans and provisional holds reuse the same structure. A banned player's entry is removed from the public set and written to lb:weekly:2026-W44:shadow instead, so ZREVRANGE for everyone else never sees them; Section 10's held top-1,000 runs wait in :pending the same way. Either player's own view is composed: their "rank" is ZCOUNT lb:weekly:2026-W44 (<their composite> +inf + 1, and their entry is spliced into their private copy of the top 100 or around-me window. Those scripts touch several keys, the side key and the public one, but always on one node, which is why the side keys share the board's primary. If the boards ever move into Redis Cluster, hash tags (lb:{weekly:2026-W44}:pending) keep them in one slot.

Quick check before moving on:


Sign in to continue reading

The rest of this lesson is available with a free account. Signing in with Google or Microsoft is free.

Sign in to read the full lesson

Sign in to MAANG.io

Use your Google or Microsoft account — no password to remember.

Continue with Google Continue with Microsoft

Please accept the terms above to continue.