fix(useSection): key lazy-section cache on block content, not release revision

Draft
PE
3d ago
+96-8

Why

useSection builds the /deco/render URL with __cb = hash(release revision, vary, href, deploymentId). The release revision changes on every CMS publish, even a decofile-only publish that doesn't redeploy. So every publish changes the URL of every lazy section on every page, and the CDN entries already warm under max-age=300, stale-while-revalidate=3600 are thrown away.

Measured on miess-01 (2026-10-13, 10:00–21:00Z, Cloudflare logs for www.miess.com.br):

  • 33 publishes in bursts. Only 1 of them redeployed; the rest were decofile-only hot-reloads.
  • /deco/render hit rate was about 33–41% in quiet windows and fell to 13–20% during publish bursts.
  • Misses where the same render URL minus __cb had been requested in the previous 65 minutes under a different __cb:
    • ~72.8k of 174k render misses (~42%), about 364 MB of origin egress plus 72.8k extra origin SSR renders.
    • 7–12k per hour in burst hours, against about 400 per hour with no publishes.
    • That's roughly 3k extra origin renders and ~15 MB per publish.
  • Most of those keys are PDP/PLP lazy sections whose own page didn't change. A home banner publish invalidated all of them.

What

The global revision is replaced with a content version: a hash of the named blocks (resolvable entries) on the section's resolve chain, for example pages-category-…, plus any named section block the section is resolved through.

  • Block hashes are memoized per block object. Decofile providers replace changed blocks with freshly parsed objects and keep identity for unchanged ones; no in-place mutation.
  • If the chain has no named block, it falls back to the revision, so behavior is the same as today there.
  • deploymentId, vary and href stay in the key, so a deploy still busts everything.

Same-shell semantics are unchanged:

  • A stale HTML shell still points at a key whose content hash differs whenever its page changed. That includes index shifts in sections, because the page block's content changes.
  • The server still renders from the current decofile, exactly as today on a miss.

Trade-off: a change to a named block that is referenced from a section's props but isn't on its resolve chain (e.g. a shared loader config) no longer busts that section's render URL. It refreshes through the normal max-age=300 + SWR revalidation, the same freshness HTML pages already have.

Tests

  • hooks/useSection.test.tsx: a new block-content __cb suite, 4 steps:
    • stable when another block is published;
    • changes when a block on the chain changes;
    • changes with the deployment;
    • falls back to the revision.
  • The new steps fail on main (2 of 4) and pass with this change.
  • Full suite: 85 passed, 0 failed.

Not validated yet (why this is a draft)

  • No end-to-end run on a real site. That needs a site pinned to this branch and a check that, after a decofile-only publish, unchanged pages keep their __cb while the edited page's sections get a new one.
  • No production measurement of hit rate after rollout.

Summary by cubic

Keys the lazy-section cache on the content of the blocks on the section's resolve chain instead of the global release revision, so a CMS publish that doesn't change a page no longer invalidates that page's warm CDN entries.

  • The __cb hash now covers the named blocks the section resolves through (e.g. the page), with hashes memoized per block object.
  • Falls back to the release revision when the chain has no named blocks; deploymentId, vary, and href remain in the key.
  • Trade-off: changes to named blocks referenced in props but not on the resolve chain skip the immediate cache bust and refresh via the normal 300s TTL + stale-while-revalidate.
  • Adds tests covering stability across unrelated publishes, invalidation on block or deployment changes, and the revision fallback.