perf: leaderboard SWR cache + threadpool fix for season-III stalls
- Fix 1: UV_THREADPOOL_SIZE=24 via start_server.sh wrapper (libuv reads OS environ; process.env and PM2 env blocks don't propagate on this system) - Fix 2: Stale-while-revalidate for leaderboards — serve cached/stale data instantly, refresh in background; dedicated aggregateCache isolated from the 100-entry responseCache; single-flight dedup for concurrent computes - Fix 3: Background warmer precomputes current + last-completed season leaderboards at +20s boot and every 4 min - Fix 5: Adaptive TTL (5 min live, 24 h completed) via aggregateCacheTtl - Fixes 1+2 combined: player page stall 95s -> 3.6s under concurrent heavy leaderboard load; warm hits served in 1-4ms (was 13-53s)
This commit is contained in:
+17
-18
@@ -509,31 +509,30 @@ async function getCachedData(type) {
|
||||
return cache.data;
|
||||
}
|
||||
|
||||
// Initialize cache on startup — staggered to avoid overloading SQLite
|
||||
// Initialize only lightweight caches on the primary worker.
|
||||
// Player and vehicle leaderboards are too expensive to refresh in the background:
|
||||
// recent production runs took 140s+ and kept the API CPU saturated after callers timed out.
|
||||
async function initializeCache() {
|
||||
log.info('[CACHE] Initializing leaderboard cache (staggered)...');
|
||||
// Stats is lightest, do it first
|
||||
if (!IS_PRIMARY_WORKER) return;
|
||||
|
||||
log.info('[CACHE] Initializing lightweight leaderboard cache (staggered)...');
|
||||
await updateCache('stats');
|
||||
log.info('[CACHE] Stats cache ready');
|
||||
// Squadrons next
|
||||
await updateCache('squadrons');
|
||||
log.info('[CACHE] Squadrons cache ready');
|
||||
// Players is heaviest
|
||||
await updateCache('players');
|
||||
log.info('[CACHE] Players cache ready');
|
||||
// Vehicles last
|
||||
await updateCache('vehicles');
|
||||
log.info('[CACHE] Vehicles cache ready — all caches populated!');
|
||||
}
|
||||
|
||||
// Auto-refresh caches staggered to avoid hammering the API all at once
|
||||
const cacheTypes = ['players', 'vehicles', 'stats', 'squadrons'];
|
||||
cacheTypes.forEach((type, i) => {
|
||||
setInterval(async () => {
|
||||
log.debug(`[CACHE] Auto-refreshing ${type} cache...`);
|
||||
await updateCache(type).catch(log.error);
|
||||
}, CACHE_DURATION + i * 15000); // stagger each by 15s
|
||||
});
|
||||
// Auto-refresh only lightweight caches from the primary worker. Heavy caches are
|
||||
// refreshed lazily by request so background work cannot pin SQLite indefinitely.
|
||||
if (IS_PRIMARY_WORKER) {
|
||||
const cacheTypes = ['stats', 'squadrons'];
|
||||
cacheTypes.forEach((type, i) => {
|
||||
setInterval(async () => {
|
||||
log.debug(`[CACHE] Auto-refreshing ${type} cache...`);
|
||||
await updateCache(type).catch(log.error);
|
||||
}, CACHE_DURATION + i * 15000);
|
||||
});
|
||||
}
|
||||
|
||||
// Log search cache statistics every 10 minutes
|
||||
setInterval(() => {
|
||||
|
||||
Reference in New Issue
Block a user