From 6db4bd9793319fc1dca10e8ad814e98bb6346409 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Sat, 13 Jun 2026 16:08:08 +0200 Subject: [PATCH] perf(cache): widen the analytics and insights stale window to an hour MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The analytics and derived-insight reads served stale for ten minutes, but a single reader visits the dashboard and insights every twenty to thirty minutes — so nearly every return landed past the window and paid the full cold rebuild instead of an instant stale serve. At an hour the return serves the prior snapshot immediately and refreshes in the background, and each stale-serve re-warms the entry; the dashboard's time-derived fields stay bounded and self-correct through the snapshot client's poll. --- src/lib/cache/server-cache.ts | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/src/lib/cache/server-cache.ts b/src/lib/cache/server-cache.ts index 580fd462b..4945d12d4 100644 --- a/src/lib/cache/server-cache.ts +++ b/src/lib/cache/server-cache.ts @@ -447,14 +447,25 @@ export const caches = { * via plain `cached` are unaffected: a marked-stale entry has * `expiresAt === now`, so their next `cached` read is a clean miss and * rebuilds fresh — only the `cachedSwr` snapshot serves stale + warms a - * background recompute. The 10-minute stale window bounds how old a - * served snapshot can be when no reader triggers a revalidation. + * background recompute. The stale window bounds how old a served + * snapshot can be when no reader triggers a revalidation. + * + * v1.16.12 — stale window widened 10 min → 1 h. A single self-hoster + * visits the dashboard / insights every ~20-30 min; the old 10-minute + * window meant nearly every return landed PAST it (a `miss`, ~1.5 s of + * synchronous rebuild) instead of inside it (a `stale`, instant + one + * background refresh). Each stale-serve re-inserts, so an active session + * stays warm regardless of the exact window; the value only governs how + * long an ABSENCE still gets an instant response. 1 h covers a normal + * break while keeping the dashboard's time-derived `nextDueOverdue` / + * tally drift bounded — and that field already self-corrects within the + * snapshot client's 120 s poll and the immediate background refresh. */ analytics: new ServerCache({ name: "analytics", maxEntries: 1000, ttlMs: 60_000, - staleTtlMs: 600_000, + staleTtlMs: 3_600_000, }), /** * v1.16.8 — stale-while-revalidate on the list bucket. The 60 s fresh @@ -596,7 +607,11 @@ export const caches = { name: "insightsDerived", maxEntries: 2000, ttlMs: 60_000, - staleTtlMs: 600_000, + // v1.16.12 — 10 min → 1 h, same rationale as the analytics bucket: the + // derived-insight aggregates are trend data (no time-critical due + // state), so a returning visitor serves an instant stale payload + + // background refresh instead of a ~1.1 s cold rebuild. + staleTtlMs: 3_600_000, }), /** * v1.5.5 — per-user insights tile layout cache. Mirrors