From dc94a160e97f5e8e322b232586b6cdfe32c38cce Mon Sep 17 00:00:00 2001 From: Gavyn Caldwell Date: Fri, 20 Mar 2026 13:38:17 -0600 Subject: [PATCH] add 24h TTL to popular apps cache and edge cache Popular apps section was serving stale data because the KV cache had no expiration and the edge cache was set to 1 week. Now both layers expire after 24 hours so download rankings stay fresh. --- db/kv-cache.ts | 4 +++- src/lib/server-fns.ts | 12 ++++++++---- src/routes/-worker-entry.ts | 6 +++--- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/db/kv-cache.ts b/db/kv-cache.ts index 5e2055d..0fcb8b1 100644 --- a/db/kv-cache.ts +++ b/db/kv-cache.ts @@ -7,6 +7,7 @@ function getKV(): KVNamespace { export async function kvCached( key: string, fn: () => Promise, + opts?: { ttl?: number }, ): Promise { const kv = getKV(); const cached = await kv.get(key, "json"); @@ -14,7 +15,8 @@ export async function kvCached( const result = await fn(); // Fire-and-forget write — don't block the response - kv.put(key, JSON.stringify(result)).catch(() => {}); + const putOpts = opts?.ttl ? { expirationTtl: opts.ttl } : undefined; + kv.put(key, JSON.stringify(result), putOpts).catch(() => {}); return result; } diff --git a/src/lib/server-fns.ts b/src/lib/server-fns.ts index a54ffcf..c465483 100644 --- a/src/lib/server-fns.ts +++ b/src/lib/server-fns.ts @@ -236,10 +236,14 @@ export const fetchRecentApps = createServerFn({ method: "GET" }) export const fetchPopularApps = createServerFn({ method: "GET" }) // eslint-disable-next-line @typescript-eslint/no-explicit-any .handler(async (): Promise => { - return kvCached(cacheKey("getPopularApps"), () => { - const db = getDb(); - return getPopularApps(db); - }); + return kvCached( + cacheKey("getPopularApps"), + () => { + const db = getDb(); + return getPopularApps(db); + }, + { ttl: 86400 }, + ); }); export const fetchComparisonBySlug = createServerFn({ method: "GET" }) diff --git a/src/routes/-worker-entry.ts b/src/routes/-worker-entry.ts index 058e351..304d936 100644 --- a/src/routes/-worker-entry.ts +++ b/src/routes/-worker-entry.ts @@ -20,9 +20,9 @@ const SITE_URL = "https://unclouded.app"; type CacheRule = { pattern: RegExp; header: string }; -// Data only changes on manual DB writes (seed/enrich), then cache:purge. -// Cache everything aggressively — 1 week fresh, 1 week stale fallback. +// Static content cached for 1 week, pages with dynamic data (homepage) for 1 day. const ONE_WEEK = 604800; +const ONE_DAY = 86400; const cacheRules: CacheRule[] = [ { pattern: /^\/search/, header: "no-store" }, @@ -40,7 +40,7 @@ const cacheRules: CacheRule[] = [ }, { pattern: /^\/(apps|alternatives|discover|desktop)?$/, - header: `public, s-maxage=${ONE_WEEK}, stale-while-revalidate=${ONE_WEEK}`, + header: `public, s-maxage=${ONE_DAY}, stale-while-revalidate=${ONE_DAY}`, }, ];