← back to AsSeenInMovies
asseeninmovies: home cache — true stale-while-revalidate. Stale hits now return cached data immediately + trigger background refresh, instead of blocking on the next 2-3s count(*). Empty-cache first hit still awaits (warmup covers it on startup).
f4b860b261fcbdea0f9d525f2426eb332feef067 · 2026-05-12 18:32:32 -0700 · SteveStudio2
Files touched
Diff
commit f4b860b261fcbdea0f9d525f2426eb332feef067
Author: SteveStudio2 <stevestudio2@SteveStacStudio.lan>
Date: Tue May 12 18:32:32 2026 -0700
asseeninmovies: home cache — true stale-while-revalidate. Stale hits now return cached data immediately + trigger background refresh, instead of blocking on the next 2-3s count(*). Empty-cache first hit still awaits (warmup covers it on startup).
---
server.js | 45 ++++++++++++++++++++++++++++-----------------
1 file changed, 28 insertions(+), 17 deletions(-)
diff --git a/server.js b/server.js
index 4875aa6..3d41d67 100644
--- a/server.js
+++ b/server.js
@@ -1368,17 +1368,17 @@ app.get('/people', async (req, res) => {
});
// Home — placeholder until tick 1 ships TMDB ingest + real UI.
-// In-memory home-page cache. count(*) on 290k asim_movies + 61k asim_people
-// is 2-3s cold each, and concurrent home hits stack up in pg_stat_activity.
-// TTL 60s — stats don't change second-to-second, and the spotted/featured
-// rails benefit from light memoization too.
-let _homeCache = null;
+// In-memory home-page cache with stale-while-revalidate semantics. count(*)
+// on 290k asim_movies + 61k asim_people is 2-3s cold each, so we never want
+// a user request to wait on that. Behavior:
+// - cache fresh (< TTL): return immediately
+// - cache stale + has data: return stale data, kick off background refresh
+// - cache empty (first hit): await the inflight refresh (warmup covers this)
+let _homeCache = { at: 0, data: null, inflight: null };
const HOME_CACHE_TTL_MS = 60_000;
-async function getHomeCache() {
- const now = Date.now();
- if (_homeCache && (now - _homeCache.at) < HOME_CACHE_TTL_MS) return _homeCache.data;
- if (_homeCache && _homeCache.inflight) return _homeCache.inflight;
- const inflight = (async () => {
+function startHomeRefresh() {
+ if (_homeCache.inflight) return _homeCache.inflight;
+ const p = (async () => {
const [s, fm, fp, fs] = await Promise.all([
pool.query(`
SELECT
@@ -1397,16 +1397,27 @@ async function getHomeCache() {
ORDER BY s.verified_at DESC LIMIT 6`)
]);
return { stats: s.rows[0], featuredMovies: fm.rows, featuredPeople: fp.rows, featuredSpotted: fs.rows };
- })();
- _homeCache = { at: now, inflight, data: _homeCache?.data || null };
- try {
- const data = await inflight;
- _homeCache = { at: Date.now(), inflight: null, data };
+ })().then(data => {
+ _homeCache = { at: Date.now(), data, inflight: null };
return data;
- } catch (e) {
- _homeCache = { at: now, inflight: null, data: _homeCache?.data || null };
+ }).catch(e => {
+ _homeCache = { ..._homeCache, inflight: null };
throw e;
+ });
+ _homeCache.inflight = p;
+ return p;
+}
+async function getHomeCache() {
+ const now = Date.now();
+ const fresh = (now - _homeCache.at) < HOME_CACHE_TTL_MS;
+ if (fresh && _homeCache.data) return _homeCache.data;
+ if (_homeCache.data) {
+ // stale-while-revalidate: serve immediately, refresh in background
+ if (!_homeCache.inflight) startHomeRefresh().catch(() => {});
+ return _homeCache.data;
}
+ // empty cache — first hit ever. Must await.
+ return await startHomeRefresh();
}
app.get('/', async (_req, res) => {
← 2261023 asseeninmovies: GET /favicon.ico fallback — same inline SVG
·
back to AsSeenInMovies
·
asseeninmovies: canonical URLs + WebSite/Organization JSON-L f89066d →