fix(pages-router): cancel deduped GSSP data waits - #2038
Conversation
commit: |
|
|
/bigbonk review for issues |
|
@james-elicx Bonk workflow was cancelled. View workflow run · To retry, trigger Bonk again. |
|
/bigbonk review for issues |
There was a problem hiding this comment.
/bigbonk review — PR #2038 (cancel deduped GSSP data waits)
I reviewed the waiter-aware dedup/cancellation logic, the router signal threading, the deferred-abort change, and the tests. The core design is sound: waiter accounting is guarded against double-release, final-waiter eviction + abort works, the settle/abort race is handled via entry-identity checks, and I could not reproduce any unhandled-promise-rejection (every entry.promise always has a .then/.catch attached through cloneSharedResponse). Targeted tests pass locally (pages-data-fetch-dedup, pages-data-prefetch, the new shims nav test).
A few non-blocking observations:
1. Comment/code mismatch on the deferred abort (router.ts:1902-1907)
// different destinations still abort the abandoned request in this turn.
const previousAbortController = routerRuntimeState.activeAbortController;
if (previousAbortController) queueMicrotask(() => previousAbortController.abort());The abort is always deferred by one microtask, including for different destinations — the comment says different destinations abort "in this turn," which is not what the code does. The behavior is still correct (the old waiter releases one microtask later and the underlying fetch is then aborted/evicted because no other waiter shares its key), but the comment overstates the timing distinction. Suggest rewording to match: supersession via navigationId is synchronous; the AbortSignal is always deferred by a microtask regardless of destination.
2. Abort cancellation correctness in resolveMiddlewareDataEffect relies on assertStillCurrent() (router.ts:1426-1441)
Now that signal is threaded into the middleware data probe, an aborted probe rejects with AbortError, which the catch { return null; } swallows into "no middleware effect." The navigation then proceeds, and only assertStillCurrent() at router.ts:1944 saves it (because in production an abort only ever fires alongside a navigationId bump). This is correct today but fragile — it couples the safety of the swallowed abort to the signal↔navigationId pairing in navigateClient. Consider re-throwing AbortError from this helper (like the gSSP path at router.ts:1532-1535 does) so cancellation is explicit rather than implicit. Not a blocker since assertStillCurrent() currently backstops it.
3. Key resolution against window.location.href (pages-data-fetch-dedup.ts:47-57)
getInflightKey resolves dataHref against window.location.href. For the normal root-relative /_next/data/... hrefs this is stable, but if a prior navigation has already replaceState'd the URL between a prefetch and the real navigation, the resolved key could differ for the same logical destination and miss the dedup. Edge case only; current fixtures don't exercise it. Worth a comment noting the assumption that dataHref is origin-stable.
Verification done
vp test run tests/pages-data-fetch-dedup.test.ts tests/pages-data-prefetch.test.ts— 8 passedvp test run tests/shims.test.ts -t "Pages Router _next/data client navigation"— 13 passed- Confirmed
gssp-redirect-target.tsx(normal-text/ "a normal page") exists for thepush-cancelledE2E assertion - Confirmed
consoleErrorsfixture auto-fails on console errors (thevoid consoleErrors;pattern matches repo convention), so the "stays silent" check is active
Overall this looks correct and well-tested. The notes above are clarity/robustness improvements, not correctness blockers.
|
Review posted to PR #2038. SummaryI reviewed PR #2038 (
I posted three non-blocking observations:
None are correctness blockers — they're clarity/robustness improvements. |
vinext parity: concurrent Pages gSSP data navigation dedup and cancellation
Date: 2026-06-15
Branch:
codex/wave2-pages-gssp-dedupBase:
origin/mainata3d2f921(fix(router): honor hybrid pages route priority (#1997))Scope
getServerSidePropsclient data navigation only./_next/datarequest identity, waiter-aware shared cancellation, and stale-response protection.Upstream Next.js references
.nextjs-ref/test/e2e/getserversideprops/test/index.test.tsshould not trigger an error when a data request is cancelled due to another navigationshould dedupe server data requests.nextjs-ref/packages/next/src/shared/lib/router/router.tsfetchNextData()resolvesdataHrefagainstwindow.location.hrefand keysinflightCacheby the resolved URL.Implementation
packages/vinext/src/shims/internal/pages-data-fetch-dedup.tsx-deployment-id.AbortController, promise, settlement flag, and active waiter count per key.packages/vinext/src/shims/router.tsCoverage
Router.push()calls issue one fetch;{ cancelled: true }route error;__NEXT_DATA__;/_next/data/...gssp-dedup-slow.json?key=samerequest and server hit1;Validation
Passed:
vp test run tests/pages-data-fetch-dedup.test.ts tests/pages-data-prefetch.test.ts tests/shims.test.ts -t "Pages Router _next/data client navigation|dedupedPagesDataFetch|prefetchPagesData"PLAYWRIGHT_PROJECT=pages-router-prod npx playwright test tests/e2e/pages-router-prod/gssp-data-dedup.spec.ts --project=pages-router-prodvinext, built the Pages fixture, and ran it withvinext start.vp check <changed files>git diff --checkReview notes