Skip to content

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

Description

@koala73

Follow-up to #4902 / PR #4905 (dashboard now polls the keyless /api/health?compact=1).

Problem

Every /api/health invocation runs a ~196-key Upstash pipeline (STRLEN + GET per check) and responses are no-store end to end (Cache-Control: private, no-store + CDN-Cache-Control: no-store). The dashboard polls it every 60s per tab with runImmediately: true on all variants — so origin probe cost scales linearly with open tabs, and the first fetch competes with the LCP-window requests (relevant to #4890, field LCP render-delay).

The underlying seed cadences are 6–24h and badge decay is computed client-side from lastUpdate, so per-minute per-tab probing buys nothing.

Fix (three parts)

  1. CDN-cache the compact form (highest leverage, server-only): compact 200s get Cache-Control: public, max-age=0, must-revalidate + CDN-Cache-Control: public, s-maxage=60. Collapses all tabs/users onto ~one origin probe per cache window per edge region. Detailed (key-gated) responses, 401s, and the REDIS_DOWN 503 stay no-store. Note: prod probes show a Cloudflare cache rule already active on this path (cf-cache-status: EXPIRED, rewritten browser TTL) — origin intent must still be correct for Vercel's edge cache and CF respect-origin; verify live headers post-deploy.
  2. Client cadence 60s → 5min (REFRESH_INTERVALS.healthFreshness): safe — FRESH_THRESHOLD default is 15min, so synthesized-OK sources (compact payloads carry no per-check age for healthy checks 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 latency goes from ≤1min to ≤6min (poll + cache window) — noise for 6–24h cadences.
  3. Idle-defer the first hydration: wrap the health-freshness scheduler registration in scheduleAfterFirstPaint (existing src/utils/after-paint.ts util) so the initial fetch leaves the startup critical path. Freshness badges are below-the-fold decoration; a 2–3s delay is invisible. Update the pinned scheduler-shape test in tests/data-freshness-health.test.mts.

Interactions

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions