Skip to content

fix(isr): use getRequestExecutionContext() from ALS in background regeneration#429

Merged
james-elicx merged 1 commit intomainfrom
opencode/brave-wizard
Mar 10, 2026
Merged

fix(isr): use getRequestExecutionContext() from ALS in background regeneration#429
james-elicx merged 1 commit intomainfrom
opencode/brave-wizard

Conversation

@james-elicx
Copy link
Copy Markdown
Collaborator

Summary

  • __triggerBackgroundRegeneration in app-rsc-entry.ts was receiving ctx as a parameter from the outer request handler, but by the time the stale-while-revalidate branch executed, that reference could be stale or absent — causing waitUntil() to never be called and the Workers isolate to terminate before the revalidation completed
  • Fix: call getRequestExecutionContext() (AsyncLocalStorage-backed) inside the function to always retrieve the live ExecutionContext for the current request
  • Same fallback added to isr-cache.ts's triggerBackgroundRegeneration so the dev-server path (which passes no ctx) also benefits

…eneration

Instead of relying on the caller to pass ctx to triggerBackgroundRegeneration,
read it from AsyncLocalStorage via getRequestExecutionContext(). This ensures
waitUntil() is always registered on the live Workers ExecutionContext, fixing
stale responses that were not triggering revalidation on Cloudflare Workers.
@james-elicx
Copy link
Copy Markdown
Collaborator Author

/bigbonk review

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented Mar 10, 2026

Open in StackBlitz

npm i https://pkg.pr.new/vinext@429

commit: 9bf61e0

@github-actions
Copy link
Copy Markdown

Example Preview Production Original
app-router-cloudflare preview production
pages-router-cloudflare preview production
app-router-playground preview production original
realworld-api-rest preview production
nextra-docs-template preview production
benchmarks preview production
hackernews preview production original

Copy link
Copy Markdown
Contributor

@ask-bonk ask-bonk Bot left a comment

Choose a reason for hiding this comment

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

Clean, well-scoped fix. The approach is correct and the implementation is solid.

Analysis

The problem: __triggerBackgroundRegeneration in the generated RSC entry received ctx as a parameter forwarded from the outer _handleRequest(request, __reqCtx, _mwCtx, ctx) scope. On Cloudflare Workers, by the time the stale-while-revalidate branch fires the async regeneration, the original ctx reference could be stale (or absent in non-Worker environments), meaning waitUntil() never gets called and the isolate terminates before regeneration completes — silently dropping the background work.

The fix: Instead of threading ctx through function parameters, call _getRequestExecutionContext() (backed by AsyncLocalStorage stored on globalThis) inside the function body. This retrieves the live ExecutionContext for the current request from the ALS scope established by _runWithExecutionContext(ctx, _run) at the top of the request handler. The same fallback pattern is applied to isr-cache.ts's exported triggerBackgroundRegeneration for the dev-server code path.

Why this works: The ALS scope is established at the outermost wrapper in the request pipeline (confirmed in app-rsc-entry.ts line ~1440 and pages-server-entry.ts line ~679). The __triggerBackgroundRegeneration call happens synchronously within that scope (it's called inline during the stale-cache branch, before the response is returned), so _getRequestExecutionContext() will always see the correct store value. The regeneration promise is registered with waitUntil() before the ALS scope unwinds.

Consistency: This aligns with the existing pattern used elsewhere in the codebase — kv-cache-handler.ts already calls getRequestExecutionContext() for its _deleteInBackground and _putInBackground methods, and the Pages Router entry (pages-server-entry.ts) already uses the same pattern for both middleware drain and ISR regeneration.

A few observations (not blocking):

  1. The isr-cache.ts change (const execCtx = ctx ?? getRequestExecutionContext()) preserves backward compatibility for callers that still pass ctx explicitly (the test at isr-cache.test.ts:250 exercises this path), which is good defensive design.

  2. The snapshot diff is large but mechanical — it's the same 3-line change repeated across all 7 test fixture permutations, which is expected.

@ask-bonk
Copy link
Copy Markdown
Contributor

ask-bonk Bot commented Mar 10, 2026

Approved PR #429. Here's a summary of the review:

Verdict: Approved

The fix correctly addresses a real bug where ctx (the Cloudflare Workers ExecutionContext) was being passed as a parameter to __triggerBackgroundRegeneration, but could be stale or absent by the time the stale-while-revalidate regeneration executed — causing waitUntil() to never fire and the isolate to terminate before regeneration completed.

The solution replaces parameter-threading with an ALS-backed _getRequestExecutionContext() call inside the function body. This is the right approach because:

  • The ALS scope is established at the top of the request pipeline, wrapping all downstream work
  • The waitUntil() registration happens synchronously within that scope
  • This matches the existing pattern already used by kv-cache-handler.ts and the Pages Router entry

The isr-cache.ts change also adds a sensible fallback (ctx ?? getRequestExecutionContext()) that preserves backward compatibility for callers that pass ctx explicitly while covering the dev-server path where no ctx is available.

github run

@james-elicx james-elicx enabled auto-merge (squash) March 10, 2026 23:58
@james-elicx james-elicx merged commit 60ca719 into main Mar 10, 2026
26 checks passed
@james-elicx james-elicx deleted the opencode/brave-wizard branch March 10, 2026 23:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant