perf(health): CDN-cache compact health, 5-min client cadence, idle-deferred first hydration (#4907)#4908
Conversation
…, 5-min cadence, idle-deferred first hydration (#4907) Every /api/health call runs a ~196-key Upstash pipeline and responses were no-store end to end, so origin probe cost scaled linearly with open tabs (60s x per-tab x all variants) and the first fetch competed with the LCP window (#4890). - api/health.js: compact 200s (the public keyless form) get Cache-Control: public, max-age=0, must-revalidate + CDN-Cache-Control: public, s-maxage=60 — all tabs collapse onto ~one origin probe per cache window per edge region. Key-gated detailed responses, 401s, and the REDIS_DOWN 503 keep no-store (caching a 401 pins an auth failure; caching a 503 masks recovery). - REFRESH_INTERVALS.healthFreshness 60s -> 5min: seed cadences are 6-24h and badge decay is client-computed; 5min stays safely under the 15min FRESH_THRESHOLD so synthesized-OK sources (age-less since #4905) cannot decay to stale between polls. - App.ts: health-freshness registration (and its immediate first run) deferred via scheduleAfterFirstPaint — freshness badges are below-the-fold decoration and must not compete with LCP requests. New tests: compact-200 cache headers + no-store pins for detailed/401/503 (handler-level, mocked Upstash pipeline); scheduler-shape pin updated to require the post-paint defer. Fixes #4907 Claude-Session: https://claude.ai/code/session_01YKrVoDp8TfEKLYXo83kPFV
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Greptile SummaryThis PR reduces origin load and LCP-window cost for the
Confidence Score: 4/5Safe to merge; all error paths (503, 401, key-gated 200) correctly bypass the new caching block and keep no-store. The cache logic is correct and well-tested. The one notable gap is that the cacheable compact response is built by spreading getCorsHeaders() output, which injects Vary: Origin and a request-scoped Access-Control-Allow-Origin into the cached headers. CDNs that respect Vary will fragment the cache per distinct client origin rather than serving a single shared entry, partially working against the stated goal. getPublicCorsHeaders() exists in _cors.js for exactly this scenario. In production, with essentially one primary origin, the impact is small, but worth addressing before the CDN behavior is treated as fully resolved. The CORS header selection in api/health.js around the compact caching block deserves a second look — using getPublicCorsHeaders() there instead of the per-request CORS headers would eliminate Vary: Origin cache fragmentation. Important Files Changed
Sequence Diagram%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
participant Tab as Dashboard Tab
participant Edge as CDN/Edge Cache
participant Origin as Vercel Origin
participant Redis as Upstash Redis
Note over Tab: After first paint (idle-deferred)
Tab->>Edge: "GET /api/health?compact=1"
alt Cache MISS (or expired after 60s)
Edge->>Origin: Forward request
Origin->>Redis: Pipeline (196 keys)
Redis-->>Origin: Results
Origin-->>Edge: "200 + CDN-Cache-Control: s-maxage=60"
Edge-->>Tab: 200 (cache MISS)
Note over Edge: Cached for 60s
else Cache HIT
Edge-->>Tab: 200 (cache HIT, no Redis probe)
end
Note over Tab: Every 5 min (was every 60s)
Tab->>Edge: "GET /api/health?compact=1"
Edge-->>Tab: 200 (likely cache HIT)
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
participant Tab as Dashboard Tab
participant Edge as CDN/Edge Cache
participant Origin as Vercel Origin
participant Redis as Upstash Redis
Note over Tab: After first paint (idle-deferred)
Tab->>Edge: "GET /api/health?compact=1"
alt Cache MISS (or expired after 60s)
Edge->>Origin: Forward request
Origin->>Redis: Pipeline (196 keys)
Redis-->>Origin: Results
Origin-->>Edge: "200 + CDN-Cache-Control: s-maxage=60"
Edge-->>Tab: 200 (cache MISS)
Note over Edge: Cached for 60s
else Cache HIT
Edge-->>Tab: 200 (cache HIT, no Redis probe)
end
Note over Tab: Every 5 min (was every 60s)
Tab->>Edge: "GET /api/health?compact=1"
Edge-->>Tab: 200 (likely cache HIT)
Reviews (1): Last reviewed commit: "perf(health): collapse health polling co..." | Re-trigger Greptile |
| let responseHeaders = headers; | ||
| if (compact) { | ||
| const { 'CF-Cache-Status': _bypassMarker, ...cacheable } = headers; | ||
| responseHeaders = { | ||
| ...cacheable, | ||
| 'Cache-Control': 'public, max-age=0, must-revalidate', | ||
| 'CDN-Cache-Control': 'public, s-maxage=60', | ||
| }; | ||
| } |
There was a problem hiding this comment.
Vary: Origin fragments the CDN cache per client origin
The cacheable spread includes getCorsHeaders(req) output, which sets Access-Control-Allow-Origin: <request-origin> and Vary: Origin. Any CDN that respects Vary (including Cloudflare and Vercel edge) will create a separate cache entry per distinct Origin value — so preview deployments, tauri://localhost, and the production origin each get their own entry. The stated goal of "one origin probe per cache window per edge region" is partially defeated.
_cors.js already exports getPublicCorsHeaders() designed exactly for public cacheable responses: it sets ACAO: *, omits Vary: Origin, and its docstring explains the same fragmentation concern. Since isDisallowedOrigin() already blocks unauthorized origins earlier in the handler and the compact form is keyless (no credentials needed), the cacheable block can safely use getPublicCorsHeaders() to produce a single cache entry per path per region.
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
… would oscillate synthesized-OK sources to stale (#4910) (#4912) Cloudflare's zone Browser-Cache-TTL override rewrites the compact health endpoint's max-age=0 to 30min (observed live post-#4908). Synthesized-OK sources compute freshness from checkedAt against the 15-min FRESH_THRESHOLD, so a browser-cached body 15-30min old flips every healthy badge to stale, then back on the next cache window — oscillating ~30min waves. fetch cache: 'no-cache' forces revalidation on every poll; Cloudflare answers from its 60s edge cache, so the #4907 origin collapse is intact and checkedAt stays well under threshold. Fixes #4910 Claude-Session: https://claude.ai/code/session_01YKrVoDp8TfEKLYXo83kPFV
Fixes #4907. Follow-up to #4902 / PR #4905.
Problem
Every
/api/healthinvocation runs a ~196-key Upstash pipeline (STRLEN+GET per check) and every response wasno-storeend to end — so origin probe cost scaled linearly with open dashboard tabs (60s cadence,runImmediately: true, all variants), and the first fetch sat in the startup critical window (relevant to field-LCP issue #4890). The underlying seed cadences are 6–24h; per-minute per-tab probing buys nothing.Changes
api/health.js): compact 200s getCache-Control: public, max-age=0, must-revalidate+CDN-Cache-Control: public, s-maxage=60, and drop the hardcodedCF-Cache-Status: BYPASSmarker. All tabs/users collapse onto ~one origin probe per cache window per edge region. Key-gated detailed responses, 401s, and the REDIS_DOWN 503 keepno-store— caching the detailed form could leak an operator view across a shared cache, caching a 401 pins an auth failure, caching a 503 masks recovery from HTTP monitors.src/config/variants/base.ts): safe margin under the 15-minFRESH_THRESHOLD, so synthesized-OK sources (age-less since PR fix(health-freshness): read the keyless compact health endpoint — stop the anon 401-per-minute flood (#4902) #4905) cannot decay tostalebetween polls. Staleness-onset detection goes ≤1min → ≤6min (poll + cache window) — noise at 6–24h seed cadences. The 15-min 401/403 suppression window from fix(health-freshness): read the keyless compact health endpoint — stop the anon 401-per-minute flood (#4902) #4905 still spans ~3 ticks.src/App.ts): thehealth-freshnessregistration (including its immediate first run) now goes throughscheduleAfterFirstPaint, keeping the fetch out of the LCP window. Freshness badges are below-the-fold decoration; the scheduler'spauseWhenHiddenbehavior is unchanged.Verification
tests/health-compact-cdn-cache.test.mjs(handler-level, mocked Upstash pipeline): compact-200 cache headers; no-store pinned on detailed-200 / keyless-401 / REDIS_DOWN-503. 4/4 pass.tests/data-freshness-health.test.mtsupdated to require the post-paint defer; 13/13 pass. Existing health server suites (11/11) green.npm run typecheck,docs-stats --check,lint:api-contractclean; tiered pre-push gate green.?compact=1headers (a Cloudflare cache rule is already active on this path —cf-cache-status: EXPIREDobserved — so live behavior needs confirming; origin intent is now correct for Vercel edge cache + CF respect-origin either way).https://claude.ai/code/session_01YKrVoDp8TfEKLYXo83kPFV