Skip to content

perf(health): CDN-cache compact health, 5-min client cadence, idle-deferred first hydration (#4907)#4908

Merged
koala73 merged 1 commit into
mainfrom
perf/health-polling-cost
Jul 5, 2026
Merged

perf(health): CDN-cache compact health, 5-min client cadence, idle-deferred first hydration (#4907)#4908
koala73 merged 1 commit into
mainfrom
perf/health-polling-cost

Conversation

@koala73

@koala73 koala73 commented Jul 5, 2026

Copy link
Copy Markdown
Owner

Fixes #4907. Follow-up to #4902 / PR #4905.

Problem

Every /api/health invocation runs a ~196-key Upstash pipeline (STRLEN+GET per check) and every response was no-store end 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

  1. CDN-cache the compact form (api/health.js): compact 200s get Cache-Control: public, max-age=0, must-revalidate + CDN-Cache-Control: public, s-maxage=60, and drop the hardcoded CF-Cache-Status: BYPASS marker. All tabs/users 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 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.
  2. Client cadence 60s → 5min (src/config/variants/base.ts): safe margin under the 15-min FRESH_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 to stale between 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.
  3. Idle-defer the first hydration (src/App.ts): the health-freshness registration (including its immediate first run) now goes through scheduleAfterFirstPaint, keeping the fetch out of the LCP window. Freshness badges are below-the-fold decoration; the scheduler's pauseWhenHidden behavior is unchanged.

Verification

  • New 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.
  • Scheduler-shape pin in tests/data-freshness-health.test.mts updated to require the post-paint defer; 13/13 pass. Existing health server suites (11/11) green.
  • npm run typecheck, docs-stats --check, lint:api-contract clean; tiered pre-push gate green.
  • Post-deploy: will re-probe prod ?compact=1 headers (a Cloudflare cache rule is already active on this path — cf-cache-status: EXPIRED observed — 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

…, 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
@koala73
koala73 enabled auto-merge (squash) July 5, 2026 17:41
@vercel

vercel Bot commented Jul 5, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
worldmonitor Ready Ready Preview, Comment Jul 5, 2026 5:44pm

Request Review

@koala73
koala73 merged commit c6db71f into main Jul 5, 2026
26 checks passed
@greptile-apps

greptile-apps Bot commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR reduces origin load and LCP-window cost for the /api/health polling path by three coordinated changes: CDN-caching the compact public form (60 s s-maxage), slowing the client poll from 60 s to 5 min, and deferring the first hydration fetch to post-paint idle time.

  • api/health.js: Compact 200 responses now carry Cache-Control: public, max-age=0, must-revalidate + CDN-Cache-Control: public, s-maxage=60; the hardcoded CF-Cache-Status: BYPASS marker is removed. Key-gated detailed responses, 401s, and REDIS_DOWN 503s all hit early-return paths before this block and keep no-store.
  • src/config/variants/base.ts: healthFreshness interval raised from 60 s to 5 min — a safe margin under the 15-min FRESH_THRESHOLD.
  • src/App.ts: Health-freshness scheduler registration wrapped in scheduleAfterFirstPaint, keeping the first Upstash pipeline out of the LCP window.

Confidence Score: 4/5

Safe 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

Filename Overview
api/health.js Adds CDN-cacheable headers for compact 200 responses; correctness is sound (503/401 early-return before the cache block), but the cacheable block spreads getCorsHeaders() which injects Vary: Origin, partially undermining the single-cache-entry-per-region goal.
src/App.ts Wraps the health-freshness scheduler registration in scheduleAfterFirstPaint, deferring the first hydration out of the LCP window; runImmediately: true is preserved so the fetch fires right after paint.
src/config/variants/base.ts Increases healthFreshness poll cadence from 60s to 5min; well within the 15-min FRESH_THRESHOLD boundary documented in the inline comment.
tests/health-compact-cdn-cache.test.mjs New 4-case handler-level test: compact-200 cacheable, detailed-200 no-store, keyless-401 no-store, compact-503 no-store. Covers all stated caching invariants cleanly.
tests/data-freshness-health.test.mts Updates the structural regex pin to require the new scheduleAfterFirstPaint wrapper; assertion message updated to reflect the post-paint intent.

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)
Loading
%%{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)
Loading

Reviews (1): Last reviewed commit: "perf(health): collapse health polling co..." | Re-trigger Greptile

Comment thread api/health.js
Comment on lines +1022 to +1030
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',
};
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 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!

koala73 added a commit that referenced this pull request Jul 5, 2026
… 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
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.

perf(health): collapse health polling cost — CDN-cache compact health (s-maxage=60), 5-min client cadence, idle-deferred first hydration

1 participant