Skip to content

test(Tokenizer): the SSR test loads Tokenizer at collection and finishes each case's sheet load - #8760

Merged
cixzhang merged 1 commit into
meta:mainfrom
vjeux:uikit/c06-tokenizer-ssr
Oct 28, 2026
Merged

cixzhang merged 1 commit into
meta:mainfrom
vjeux:uikit/c06-tokenizer-ssr

Conversation

@vjeux

@vjeux vjeux commented Oct 27, 2026

Copy link
Copy Markdown
Contributor

Review only this PR's change: vjeux/astryx@6b96fc3...7b45858

Maintainer impact

On a busy machine, Tokenizer.ssr.test.tsx fails in full test runs and passes alone, so it shows up as a red on pull requests that never touch Tokenizer. Its coarse-pointer case times out at its 30 s budget, and the fine-pointer case after it then reports "This root received an early update, before anything was able hydrate".

What led here: the red appeared in our full runs after #7977 added the file. Each case hydrates a fresh module graph (vi.resetModules(), then an import of the internal Tokenizer entry). vi.resetModules() only re-runs modules; their transforms stay cached. So the first case alone paid the cold transform of Tokenizer's graph inside its budget: 3.4 s of the file's 3.5 s alone, many times that under load. Its work then kept running, and its on-demand sheet load resolved during the next case's hydration.

Intended invariant

A case's time goes to the hydration it checks, and none of its work reaches the next case.

Change

Options weighed:

  • Raise the 30 s budget: it hides the cost and still fails on a slow enough machine.
  • Drop vi.resetModules(): the cases would share one module graph, so a later case would no longer start from a fresh server process, which is what the test checks.
  • Load the graph while the file is collected, which is untimed, as test(DropdownMenu): the SSR test loads the menu and its sheet at collection #8685 does for DropdownMenu's SSR test: each case keeps its fresh graph and only re-runs the modules. Chosen.

So the Tokenizer entry and its sheet load at collection, a coarse-pointer case finishes the sheet load its hydration starts before it unmounts, and a case that ends early has its hydration finish before the next case starts.

Evidence

How to check: cut the three budgets to 3 s (}, 3000);) and run pnpm vitest run packages/core/src/Tokenizer/Tokenizer.ssr.test.tsx.

  • Failure before the change: main's file times out the coarse-pointer case and the fine-pointer case reports the early update, in every run.
  • Success after the change: all three cases pass, in about 0.11 s, 0.05 s and 0.03 s.
  • Mutation-sensitive proof: with the first load kept inside the first case, that case still times out at 3 s, while the next cases hydrate cleanly.
  • Product/runtime behavior verified unchanged: test-only.

Scope

  • No intended public API, product behavior, visual direction, or policy change.
  • Product changes discovered during the work were removed or split.
  • Public text and artifacts contain no internal Meta context.

Testing

  • Tokenizer tests and the announcement adopters' test: 165/165.
  • A full core run on a loaded machine (load average 65) failed both cases with the old file.
  • prettier, eslint, core typecheck, check:changesets.

…hes each case's sheet load

Each case hydrates a fresh module graph (`vi.resetModules()`, then an import
of Tokenizer's entry). The first case paid the graph's transform inside its
30 s budget, about 3.4 s alone and many times that on a busy machine, where
it timed out. Its work then kept running, and a coarse-pointer case's
on-demand sheet load resolved during the next case's hydration, which
reported "This root received an early update".

The entry and its sheet now load while the file is collected, which is
untimed, so each fresh graph only runs the modules again. A coarse-pointer
case finishes the sheet load its hydration starts before it unmounts, and a
case that ends early has its hydration finish before the next case starts.
@vercel

vercel Bot commented Oct 27, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
astryx Ready Ready Preview Oct 27, 2026 11:11pm UTC

Request Review

@astracat-bot

astracat-bot Bot commented Oct 27, 2026 •

Copy link
Copy Markdown

AI review status for this pull request.

Review status Updated
🟢 Reviewed (for maintainers only) Oct 27, 2026, 11:23 PM UTC

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Meta Open Source bot. label Oct 27, 2026

@astracat-bot astracat-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Recommending approval: loading Tokenizer's module graph at collection keeps the cold transform out of the first SSR case's budget, and the coarse-pointer cases still fail when the FR16 hydration path is broken. With every budget cut to 1 s, main times out both coarse-pointer cases, while this branch passes all three, each in 0.1 s or less. One optional hardening: wrap the afterEach cleanup in try/finally (lines 39–48), so a case that times out and then throws cannot leave its focused trigger and stubs for the next case. Also note that the description's 3 s recipe passes on main on a machine where the first case takes about 2.8 s; a 1 s cut reproduces the failure reliably.

[Automated review]

Comment on lines +42 to +44
if (hydrating != null) {
await hydrating;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optional hardening: if hydrating rejects (a case that timed out and then failed), this await throws before the cleanup below, so the failed case's container, focus and stubs reach the next case. Putting the cleanup in a finally keeps the error on the timed-out case and still resets the DOM:

try {
  if (hydrating != null) {
    await hydrating;
  }
} finally {
  vi.restoreAllMocks();
  vi.unstubAllGlobals();
  document.body.replaceChildren();
}

@cixzhang
cixzhang merged commit ad19aec into meta:main Oct 28, 2026
59 checks passed
@vjeux
vjeux deleted the uikit/c06-tokenizer-ssr branch October 28, 2026 00:07
vjeux added a commit to vjeux/astryx that referenced this pull request Oct 28, 2026

This branch was successfully deployed

1 active deployment
Preview — 7b458581 Deployed Oct 27, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Meta Open Source bot.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants