fix(content-cache): force sync refresh on operator invalidation#933
Conversation
markGitHubContentStale and markDocsArtifactsStale set staleAt to the epoch as a "force refresh on next read" sentinel — used by the admin invalidate button and the GitHub push webhook. The SWR readers in getCachedGitHubContent and getCachedDocsArtifact ignored that intent: when a row had positive cached content but was stale, they returned the cached value and fire-and-forgot a background refresh. On Netlify Functions the background promise often never lands, the rendered page goes back into the CDN with stale content, and the next CDN revalidation pulls the same stale row — invalidation effectively never converges. Add isForciblyStale(staleAt) that recognizes the epoch sentinel, and route forcibly-stale rows past the SWR branch into withPendingRefresh so the very next request awaits a fresh origin fetch. Natural TTL expiry still SWRs as before. The stale-on-origin-error fallback at the bottom of withPendingRefresh is preserved, so a failed GitHub call after invalidation still returns the previously cached value instead of erroring.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughThis PR adds epoch-based cache invalidation sentinel handling to the GitHub content and docs artifact cache layer. A new ChangesCache invalidation sentinel
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ESLint
ESLint skipped: no ESLint configuration detected in root package.json. To enable, add Comment |
Summary
markGitHubContentStale/markDocsArtifactsStaleusestaleAt = new Date(0)as a "force refresh on next read" sentinel — that's what the admin Invalidate button and the GitHub push webhook both call. The admin UI even spells out the contract: "The next docs request for that repo will fetch fresh content from GitHub and rebuild derived artifacts."getCachedGitHubContent/getCachedDocsArtifactdidn't honor it. When a row had positive cached content but was stale, they returned the cached value and fire-and-forgot a background refresh. On Netlify Functions that background promise is not guaranteed to land, the rendered page goes back into the CDN with stale content, and the next CDN revalidation pulls the same stale DB row — invalidation effectively never converges. Both reported symptoms (missing Deferred Hydration sidebar entry in Start docs and missing Astro Islands FAQ section in the guide body) trace to this single path:config.jsonand the markdown file both flow throughfetchRepoFile→getCachedGitHubTextFile→getCachedGitHubContent.isForciblyStale(staleAt)that recognizes the epoch sentinel and route forcibly-stale rows past the SWR branch intowithPendingRefresh, which awaits a fresh origin fetch synchronously. Natural TTL expiry (24h positive / 15min negative) still SWRs as before. The stale-on-origin-error fallback insidewithPendingRefreshis preserved, so a failed GitHub call right after invalidation still returns the previously cached value instead of erroring.Test plan
pnpm test(tsc + lint) — clean for the touched file; pre-existing warnings elsewhere are unaffectedpnpm run test:smoke— passed locally (10/10) via the pre-commit hooktanstack/router, then load/start/latest/docs/framework/react/deferred-hydration(or any updated doc) — confirm the page reflects the latest GitHub content within one origin revalidation cycle (rather than perpetuating the stale render)Notes / follow-up
The Netlify CDN still has its own SWR windows in front of the SSR (
max-age=600, stale-while-revalidate=3600on latest docs pages;max-age=300, stale-while-revalidate=300on the config server fn). With this fix the staleness is now bounded — once the CDN revalidates against origin, it will pick up fresh content. Before this fix the CDN kept re-caching stale renders indefinitely. Making invalidation feel instant requires also issuing a Netlify cache purge from the admin/webhook paths — happy to follow up with that as a separate PR once we decide on the token/IAM story.Summary by CodeRabbit