mirror of
https://github.com/cloudflare/vinext.git
synced 2026-05-09 08:25:34 +02:00
[GH-ISSUE #959] Don't block route handler response on background SWR regeneration (Node runtime parity) #207
Labels
No labels
enhancement
enhancement
good first issue
help wanted
nextjs-tracking
nextjs-tracking
pull-request
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
starred/vinext#207
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Originally created by @github-actions[bot] on GitHub (Apr 29, 2026).
Original GitHub issue: https://github.com/cloudflare/vinext/issues/959
Next.js Change
In a Node-runtime app route handler,
'use cache'and fetch-cache stale-while-revalidate were blocking the client response on the full background regeneration. A second request after the cache had gone stale did not return the stale value promptly — it waited the entire regen duration and then returned the freshly regenerated value. Edge route handlers were unaffected because they route throughedge-route-module-wrapper.ts, which handspendingWaitUntildirectly toevt.waitUntiland never awaits it before the response completes.Commit:
521ae96PR: #93189
Fixes: #93146
What Changed
In
packages/next/src/build/templates/app-route.ts, the route-handler response template:let pendingWaitUntil = context.renderOpts.pendingWaitUntil, cleared it when handing off toctx.waitUntil, but then passedcontext.renderOpts.pendingWaitUntil(the unmutated property, not the cleared local) intosendResponse.pipe-readable.tsawait, and the same promise was awaited twice:ctx.waitUntilregistered it redundantly andpipe-readable.tsstill heldres.endopen for the full revalidation.The fix is a one-line change: pass the local
pendingWaitUntil(which isundefinedonce handed off) intosendResponse, so Node route handlers match both app pages and edge route handlers, restoring the original intent of #74164.The bug was originally introduced when route handler response handling was copied verbatim into the route template in #80189.
Impact on vinext
vinext implements its own App Router route handler runtime and ISR/SWR plumbing. The Next.js architecture relies on a clear split:
waitUntilis available (Cloudflare Workers, Vercel) → background revalidations run out of band viactx.waitUntiland the response stream completes immediately.pipe-readable.tskeepsres.enddeferred until pending revalidates settle, so minimal-mode deployments stay alive long enough for writes to persist.On Cloudflare Workers, vinext should always be in the "platform
waitUntilavailable" branch —ctx.waitUntilis the standard Workers API. The risk to vinext is whether its route handler implementation correctly hands off pending revalidates toctx.waitUntiland does not alsoawaitthem before completing the response. If it does both (or neither), the symptom is identical to the Next.js bug: SWR appears to block the client.Suggested Action
cloudflare/worker-entry.tsand any Pages Router route equivalents) to confirmpendingWaitUntil/pendingRevalidatesare forwarded toctx.waitUntiland not also awaited before the response is flushed.'use cache'regen and fetch-cache regen, since both feed into the same pending-revalidate queue.test/e2e/app-dir/use-cache-swr/app/delayed-routethat asserts total response time on the second (stale) request is fast — not the full regen duration.waitUntilForEnd; only the terminating chunk was delayed.