Skip to content

fix(website): isolate Lazy fallback-discovery from page cache state - #1646

Open
igoramf wants to merge 1 commit into
mainfrom
fix/lazy-cache-state-isolation
Open

fix(website): isolate Lazy fallback-discovery from page cache state#1646
igoramf wants to merge 1 commit into
mainfrom
fix/lazy-cache-state-isolation

Conversation

@igoramf

@igoramf igoramf commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Problem

Storefront home pages (e.g. www.farmrio.com.br/) were served with
Cache-Control: no-store while PLP/PDP cached normally
(public,max-age=180,stale-while-revalidate=3600). CDN full-page cache
was fully disabled on the highest-traffic page.

Root cause

website/sections/Rendering/Lazy.tsx defers rendering of its child
section, but its loader resolves that child once up front just to
read its LoadingFallback and metadata:

const resolvedSection = await resolveSection({}, { ... });

The engine resolves a deferred value with { ...context, ...partialCtx }
(engine/core/mod.ts). Passing {} means the child's whole resolve
subtree runs against the parent page's request state — its vary,
flags, response and bag. So any deferred section whose loader is
no-store (or returns a null cache key) sets ctx.vary.shouldCache = false on the page (blocks/loader.ts), and the middleware then writes
no-store for the entire page (runtime/middleware.ts,
applyPageCacheDecision).

A home with ~70 Lazy sections only needs one of them to touch a
per-user loader (cart, weather, session, …) for the whole page to become
uncacheable — even though that section is deferred and its real render
happens later in a separate partial request.

Fix

Resolve the fallback against an isolated request state
(newIsolatedRequestState()), covering exactly the four keys the engine
threads as request state (bag, flags, response, vary — see
runtime/utils.ts). Speculative mutations during fallback discovery are
discarded. The eager partial still re-resolves the section with the real
request state, so personalized/deferred sections stay personalized.

Tests

website/utils/isolatedRequestState.test.ts asserts two isolated states
don't share vary/flags/response/bag and that side effects on one
don't leak to the other. deno test passes; deno check clean on both
changed files.

🤖 Generated with Claude Code


Summary by cubic

Fixes a bug where deferred sections in Lazy could disable full-page caching by leaking per-user loader mutations into the page state. Fallback discovery now runs in an isolated request state, restoring CDN caching on homepages while keeping personalization intact.

  • Bug Fixes
    • Resolve Lazy fallback with newIsolatedRequestState() so bag, flags, response, and vary mutations don’t affect the page.
    • Keep the eager partial re-resolving with the real request state to preserve personalization.
    • Added tests to ensure no leakage between isolated and real request states.

Written for commit 0bb69c2. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • Bug Fixes

    • Improved lazy-loaded section handling to prevent speculative resolution from affecting the parent page’s caching, response headers, or feature state.
    • Preserved existing eager and lazy rendering behavior.
  • Tests

    • Added coverage confirming isolated request state remains independent across separate resolutions.

…che state

Lazy's loader resolves the selected section once during the loader phase
solely to discover its LoadingFallback and metadata. That resolution ran
against the parent page's request state (`resolveSection({}, ...)` spreads
the ambient context), so a deferred section's loaders/matchers could set
`vary.shouldCache = false`, push flags, or emit Set-Cookie on the page —
disqualifying the whole page from the shared CDN cache even though the
section is deferred.

Pages with many Lazy sections (e.g. a storefront home with ~70 deferred
sections, one of which reads a no-store loader such as cart/weather) were
served `Cache-Control: no-store` while PLP/PDP cached normally.

Resolve the fallback against an isolated request state (`bag`, `flags`,
`response`, `vary`) so speculative mutations are discarded. The eager
partial still re-resolves the section with the real request state, so
personalized sections stay personalized.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

Tagging Options

Should a new tag be published when this PR is merged?

  • 👍 for Patch 0.159.8 update
  • 🎉 for Minor 0.160.0 update
  • 🚀 for Major 1.0.0 update

@coderabbitai

coderabbitai Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 13e4c68f-09e1-4fab-94b2-4057744c417d

📥 Commits

Reviewing files that changed from the base of the PR and between a0a57a1 and 0bb69c2.

📒 Files selected for processing (3)
  • website/sections/Rendering/Lazy.tsx
  • website/utils/isolatedRequestState.test.ts
  • website/utils/isolatedRequestState.ts

📝 Walkthrough

Walkthrough

Changes

Lazy resolution isolation

Layer / File(s) Summary
Isolated request-state factory and validation
website/utils/isolatedRequestState.ts, website/utils/isolatedRequestState.test.ts
Adds newIsolatedRequestState with independent resolver state for bags, flags, response headers, and cache-vary tracking, plus tests for defaults and non-aliasing.
Deferred loader integration
website/sections/Rendering/Lazy.tsx
The lazy loader resolves deferred sections with isolated request state after aborting the original controller.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related PRs

  • deco-cx/apps#1369: Also modifies deferred section resolution in Lazy.tsx, including URL-based forcing and metadata guards.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description does not follow the required template and is missing the Issue Link, Loom Video, and Demonstration Link sections. Add the required template sections: What is this Contribution About?, Issue Link, Loom Video, and Demonstration Link, with the relevant details and links.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: isolating Lazy fallback discovery from page cache state.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/lazy-cache-state-isolation

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@cubic-dev-ai cubic-dev-ai 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.

No issues found across 3 files

Re-trigger cubic

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