Repository navigation
Open
Description
Follow-ups to the on-demand (ISR-style) prerender feature (#640) that an independent API review placed after the merge. None blocks #640.
- Intercept variants are not refreshed.
PrerenderKey.interceptis reserved. A plain on-demand route used as an intercept target returns 404 in production for a param that exists only in the overlay: the overlay is skipped for the intercept variant, the build-time manifest misses, and the in-request producer gate refuses to render. Documented as a limitation in the skills by feat(router): on-demand (ISR-style) prerender #640; decide whether to refresh intercept variants or warn. - Delete or tombstone through the trigger. (done in feat(router): remove a page from on-demand prerender (#1060) #1070:
prerender.remove()and the "removed" marker)WritablePrerenderStore.deleteexists but nothing calls it. A refresh whose handler callsnotFound()keeps the old entry (the last good entry is served, as Next.js ISR does), so a deleted product keeps its page until a deploy. Needs an explicit way to remove an entry. - Negative caching for overlay misses, and an isolate memo for KV tag markers. A KV-backed overlay hit costs 1 + N KV reads (one per tag marker); a miss on every request for a never-refreshed param reads KV each time.
- KV marker TTL and router-id scoping. (router-id scoping done in fix(prerender): scope tag markers by router id and pin multi-router prerender and warming #1067; marker TTL still open, to be decided with entry lifecycle) Markers have no TTL today (the runtime store uses
tagInvalidationTtl), and are not scoped by router id (the runtime store's markers are not either, so this is parity for now). A TTL must outlive entries that are never refreshed. - A Vercel store. On the Node and Vercel presets the overlay is a per-instance memory store today.
- Warn when
createRSCHandler({ version })andcreateRouter({ version })disagree (no change needed, checked in fix(prerender): stamp an on-demand entry with its render's start; browser tests for many(), render-failed and skipped-passthrough (#1072, #1060) #1074: the trigger and the handler both key the overlay from the router's version, so the two options cannot move it apart), since the trigger and the handler must compute the same overlay key version. - E2e for
.many(),render-failedandskipped-passthrough(done in fix(prerender): stamp an on-demand entry with its render's start; browser tests for many(), render-failed and skipped-passthrough (#1072, #1060) #1074, both apps, dev and production, including a build-baked param that is later declined), covered by unit tests only today.
Metadata
Metadata
Assignees
Labels
No labels
Activity
ivogt commentedon Oct 6, 2026
Status after the pre-release work of 2026-10-06:
markStaleis per router). KV marker TTL: tried and dropped on review (an entry without attlnever goes stale by itself, so an expired marker would silently drop an invalidation); still open here, to be decided with entry lifecycle.prerender.remove()plus a "removed" marker written by a refresh that hitsnotFound()(branchfeat/prerender-remove).updateTag()needs one), so a stored document serves until it expires; direction: an implicit per-page tag on documents of on-demand routes that the trigger invalidates through the store it already resolves; (b) intercept variants of a removed page still serve the build-time variant (goes with the intercept-variant item above).feat(router): remove a page from on-demand prerender (#1060) (#1070)
ivogt commentedon Oct 7, 2026
Status 2026-10-07:
prerender.remove()and the "removed" marker), fix(prerender): scope tag markers by router id and pin multi-router prerender and warming #1067 (per-router tag markers, multi-router prerender and warming pinned), feat(router): MemorySegmentCacheStore({ scope }) lets a single-process server be warmed, plus e2e for the local-store refusal #1066 (MemorySegmentCacheStore({ scope }), browser test for the refused local-store warm), fix(router): stamp cache data entries with the execution start, not the write time (#1068) #1073 (cache data entries stamped with the execution start, CFCacheStore stamps data entries at write time: a render that started before a tag invalidation is stored as valid #1068).storedAtat write time), the check whether acreateRSCHandler({ version })/createRouter({ version })mismatch can still move the overlay key (the handler already resolves the prerender version from the router, so this may need no change), and browser tests for.many(),render-failedandskipped-passthrough.fix(prerender): stamp an on-demand entry with its render's start; bro…