perf(cache): probe-based cache admission and an off-request board warmer - #83
Merged
Conversation
Opening aeman after any >10-minute break hung on a 20-30s progress bar. Two causes stacked: - The shared board cache is (correctly) gated per login, but the only way a login could prove access was a full token-scoped board load — a dozen sequential GraphQL pages on a large board. Every engineer coming back from a break personally re-paid that load to be admitted into a cache their teammates kept warm. - Overnight, with every laptop asleep, no watch connection survived to keep the cache alive, so the first open of the morning always started from a dead cache. Meanwhile, during the day, every open tab kicked a stale revalidation from its 30s ping ticker — a near-continuous full reload of a multi-page board. Now a login reaching a warm cache proves access with a single project-id probe (~0.5s) instead of the full load, and one server-side warmer loop per board refreshes the cache every few minutes while the board has watchers or was read within the idle window — carrying it through the night — replacing the per-tab ping revalidation entirely. The warmer runs on the token of the request that started it, stops on the first failure (a revoked token must not keep polling), and never vouches for anyone's access. Net effect on a ~1400-card board: first open after a break drops from ~28s to under a second, and steady-state GitHub traffic goes down. The shared test fakes also become goroutine-safe: the async title resolve and the warmer hit them from background goroutines, which the race detector rightly flagged. Assisted-By: Claude Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
Three independent reviews (concurrency/lifecycle, security, perf/ops) of the previous commit converged on the same root problems; this commit resolves every blocker and major, and the minors. Mutations no longer re-pay the full board load. boardFreshFor now matches the warmer's watched cadence (3 minutes): the warmer is what keeps the cache inside the fresh window, so both reads and mutations land on cacheFresh instead of blocking on a multi-page reload — the previous 30s window left every mutation outside it most of the time (review blocker). A mutation that queues behind the warmer's reload now also rides the freshly installed board, proving its own access by probe, instead of loading again. An aging access proof no longer forces a full reload. cached() reports 'serve fresh, but re-prove' and the proof refreshes via a background probe (deduplicated per login) — previously the 60s..10min proof-age window pushed every read into a full background reload, which negated most of the intended GitHub-traffic win (review major). A probe that positively reports access gone drops the proof; the boardStaleMax ceiling still bounds a revoked token's ride exactly as before. The warmer no longer outlives its owner or dies of a single 502. Its identity rotates to the most recent successful loader, is bound to that request's web session, and stops when the session ends (logout or TTL) — a captured OAuth token no longer polls GitHub after its owner left (security major). MCP-request tokens never power the warmer: they have no session to bind their lifetime to. Transient refresh failures now skip a tick (with a Sync broadcast, matching revalidate's contract) instead of killing the loop — one hiccup at 02:00 used to silently guarantee the next morning's 30s cold open (perf major); persistent failure or access-gone still stops it, with a log line. Watcher-less boards refresh on a slower idle cadence to spare the captured token. Also per review: lastRead is stamped only on successful serves, so an unauthorized caller cannot extend the warm window; the warming flag is cleared in the same critical section as the exit decision, closing a lost-wakeup race; markAuthed sweeps expired proofs before the empty-login return so the warmer's installs keep the map clean; warm loads are bounded by a timeout; and the probe now has a parity test running identical GitHub fixtures through CheckBoardAccess and the full load, pinning the two verdicts together — that lockstep is the security argument for admitting users by probe. Assisted-By: Claude Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
kvaps
marked this pull request as ready for review
August 9, 2026 23:08
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Opening aeman after any break longer than ten minutes hung on a 20-30s progress bar. Measured on the production-size board (~1400 cards): a full load is ~15 sequential GraphQL pages at ~2s each — sequential because GitHub's cursor pagination cannot be parallelized — so ~28s wall-clock. Two designs made that load far more frequent than it had to be:
Change
Probe-based admission. Proving that a token can read the board costs one project-id GraphQL query (~0.5s), not a full load. A login reaching a warm cache is admitted by that probe; an aging proof refreshes the same way in the background while the read is served fresh. The full load remains the authorization path wherever the probe is unavailable or fails, and a probe that positively reports access gone drops the proof. A parity test runs identical GitHub fixtures (not-found, partial errors, rate-limit, 401/403, insufficient scopes, 502) through both the probe and the full load and pins their verdicts together — the probe is exactly as strict as the load it stands in for.
Off-request warmer. One server-side loop per board refreshes the cache on the cadence of the fresh window while the board has watchers, and on a slower idle cadence for 16h past the last read — carrying the cache through the night, so the morning's first open is served instantly. It replaces the per-tab ping revalidation entirely. The warmer's identity rotates to the most recent successful loader and is bound to that user's web session: it stops on logout or session expiry, never runs on MCP-request tokens (nothing to bind their lifetime to), tolerates transient upstream failures by skipping a tick, and stops with a log line on persistent failure or access loss. Warm refreshes never vouch for anyone's access.
Mutations benefit directly: with the warmer keeping the cache inside the fresh window, reads and mutations land on the cached board instead of blocking on a reload.
Review
The change went through three independent adversarial reviews (concurrency/lifecycle, cache-authorization security, perf/operations). The security review verified empirically that the probe admits no one the full load would reject, and found no bypass of the per-login cache gate. All blockers and majors they raised are addressed in the second commit; the notable ones: mutations re-paying full loads (fresh window now matches the warmer cadence), the warmer dying overnight on a single 502 (bounded retries), a captured OAuth token outliving its owner's session (session-bound, rotating source), and an unauthorized caller being able to extend the warm window (reads stamp it only when actually served).
Numbers
On the ~1400-card board: first open after a break drops from ~28s to milliseconds (probe ~0.5s when a re-proof is needed); the only remaining cold load is the very first request after a server restart. Steady-state GitHub traffic drops from roughly one full reload per active tab per minute to one per board per 3 minutes (8 minutes when nobody is watching).
Known trade-offs
Testing
TestBoardCacheProbeAdmitsSecondUser,TestBoardCacheProbeDeniedFallsThrough— probe admits exactly one cheap check, denial falls through to the caller's own load.TestCachedAuthz— the full state matrix including the new re-proof flag.TestBoardCacheWarmerFollowsWatchers— refreshes on its own, outlives watchers within the idle window, stops when both conditions lapse.TestCheckBoardAccessMatchesFullLoad— 10-fixture probe/load parity table.-race.