Section 12: Beyond the Blitz Board - Follow-up Feature Evolution
Executive Summary: The product team has six asks: daily chess, variants and Chess960, play the computer, live broadcasts of over-the-board events, an opening explorer, and team battles. None of them needs a rewrite, because each one lands on a decision we wrote down with a flip trigger. Daily chess flips the single-owner actor choice: game actors hibernate and flag timers move to a durable scheduler. The explorer flips the archive choice: a derived index gets built in batch over it. Variants add rules engines and rating pools, and thin variant pools trip the matchmaker's thin-pool trigger. Bots, broadcasts and team battles flip nothing. Every API change is a new field or a new endpoint, and a v1 client keeps working.
The interviewer leans back. "The platform is fair and it's alive. Product wants more. How much of this survives?"
You: "Most of it. Before each feature I'll say which earlier decision it touches. If none, it's just new code on the old path."
Daily Chess: The Actor Goes to Sleep
In daily chess you get days per move, not minutes per game. The single-owner actor choice said one in-memory actor owns each live game and arms its own timers, and its flip trigger named this exact case. An actor that sits idle for three days is a bad deal. It holds ~2 KB of memory to do nothing, and it uses up a slot under the 10K-games-per-node cap, a cap we set for blast radius. Worse, every deploy would turn into a failover of thousands of sleeping games.
So the actor hibernates. State lives only in live_games, and the item gets no owner_server while nobody is moving. When Omar opens the game and plays, his gateway asks placement for the owner and finds none. Placement then assigns one, bumping owner_epoch, and the new actor loads the item with one read. That's the Section 10 failover path, run on purpose. The move then goes through the same pipeline as a blitz move: validate, conditional write ply = :n AND owner_epoch = :e, publish. After an idle spell with no messages the actor unloads itself, so two players trading moves within minutes don't force a reload each time.
Timers can't live in memory for days, so they move to a durable scheduler: a table of (deadline, game_id, ply) entries, polled in deadline order. When an entry fires, the actor materializes and runs Section 7's flag check: is it still that side's turn at ply n? If a move got in first, the entry is stale and gets ignored. The scheduler can therefore fire at least once and the check absorbs duplicates. The same scheduler sends Notifications a reminder before the deadline, by push or email.
Interviewer: "What about gateway timekeeping?"
You: "It still runs, but it no longer matters. Gateway timekeeping exists so that 120 ms of backbone transit doesn't land on a three-minute clock. Against a three-day deadline it's noise, so the deadline is just the server's commit time plus the allowance. The rating pool is new too: daily gets its own."
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