Stabilize prerender "distinct pages per realm" against cross-affinity steal#4856
Merged
Merged
Conversation
… steal
When `#ensureStandbyPool` can't conjure a fresh standby (transient
browser-context creation failure under CI load), the brand-new-affinity
fallback in `PagePool.#selectEntryForAffinity` reassigns an idle tab
from another realm. `#reassignAffinityTab` keeps the donor's `pageId`,
so two different realms can momentarily share the same Chrome page —
the very state-leak risk the test guards against.
Three pieces:
1. Add `Prerenderer.warmStandbys()` (wraps `PagePool.warmStandbys`).
2. Await it from `disposeAllRealms` in the formats-and-pooling
module's beforeEach so every affinity-pooling test starts on a
fully-warm standby pool — r2's getPage always finds a standby
and never falls to the cross-affinity steal.
3. Warn-log inside `getPage` when entry.affinityKey != requested
affinityKey (the cross-affinity-steal signature) with live
standby/creating/active/maxPages counts, and enrich the test
assertion to dump pageIds, timings.waits, and queue snapshot.
If the underlying refill failure ever resurfaces, CI logs will
show exactly where the standby pool was when the race fired.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
Pull request overview
Stabilizes the does not reuse across different realms (default) prerender flake by ensuring the standby page pool is fully warmed between affinity-pooling tests, and adds diagnostics around the cross-affinity-steal fallback so future regressions are easier to triage.
Changes:
- Add
Prerenderer.warmStandbys()wrapper overPagePool.warmStandbysand await it from the affinity-poolingdisposeAllRealmsbeforeEach. - Log a
warninPagePool.getPagewhen an entry comes back tagged for a different affinity (the cross-affinity-steal signature), including standby/creating/active counts. - Enrich the
distinct pages per realmassertion message with both pageIds, wait timings, and a queue depth snapshot.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| packages/realm-server/tests/prerendering-test.ts | Awaits warmStandbys() between realm disposals and expands the distinct-pages assertion message with diagnostics. |
| packages/realm-server/prerender/prerenderer.ts | Adds public warmStandbys() method delegating to PagePool. |
| packages/realm-server/prerender/page-pool.ts | Adds log.warn breadcrumb when an entry's affinity differs from the requested key prior to reassignment. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Contributor
lukemelia
approved these changes
May 18, 2026
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.
Summary
does not reuse across different realms (default)inprerendering-test.ts(failing check, shard 2 first attempt). Bothr1.pool.pageIdandr2.pool.pageIdcame back equal — the signature of the brand-new-affinity cross-affinity-steal fallback inPagePool.#selectEntryForAffinity. That path repurposes an idle donor tab when#ensureStandbyPoolcan't produce a fresh standby (a transient browser-context creation failure under CI load);#reassignAffinityTabpreserves the donor'spageId, so two different realms momentarily share the same Chrome page.Prerenderer.warmStandbys()(thin wrapper overPagePool.warmStandbys) andawaitit from theformats and poolingmodule'sdisposeAllRealmsbeforeEach. Every affinity-pooling test now starts with a fully warm standby pool, so r2'sgetPagealways finds a standby and never reaches the steal.PagePool.getPage:log.warnwhenentry.affinityKey !== requested affinityKey(uniquely the cross-affinity-steal signature) with livestandbys/creating/active/maxPagescounts and both affinity keys.pageIds, bothtimings.waitsblocks, andgetQueueDepthSnapshot()so a future failure shows exactly which step ran short.Test plan
prerendering-test.tsshard 2 — the affinity-pooling subtree is deterministic on the standby path now.#ensureStandbyPoolis the underlying problem (and which counts looked wrong when the steal fired).🤖 Generated with Claude Code