From 449568e3a2eb0d3c1ed0200180be1385ba9b6636 Mon Sep 17 00:00:00 2001 From: k00b Date: Tue, 8 Oct 2024 17:58:15 -0500 Subject: [PATCH] don't let pending cache to build up --- lib/fetch.js | 53 ++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 47 insertions(+), 6 deletions(-) diff --git a/lib/fetch.js b/lib/fetch.js index c4267df5..55b6671e 100644 --- a/lib/fetch.js +++ b/lib/fetch.js @@ -38,33 +38,74 @@ class LRUCache { export function cachedFetcher (fetcher, { maxSize = 100, cacheExpiry, forceRefreshThreshold, keyGenerator }) { const cache = new LRUCache(maxSize) + console.log(`[CACHE] Initializing cache: maxSize=${maxSize}, cacheExpiry=${cacheExpiry}, forceRefreshThreshold=${forceRefreshThreshold}`) + + if (!keyGenerator) { + throw new Error('keyGenerator is required') + } + + let totalFetches = 0 + let cacheMisses = 0 + let cacheHits = 0 + let backgroundRefreshes = 0 + + setInterval(() => { + console.log(`[CACHE] Stats: total=${totalFetches}, hits=${cacheHits}, misses=${cacheMisses}, backgroundRefreshes=${backgroundRefreshes}, cacheSize=${cache.cache.size}`) + }, 60000) // Log stats every minute return async function cachedFetch (...args) { - const key = keyGenerator ? keyGenerator(...args) : JSON.stringify(args) + const key = keyGenerator(...args) const now = Date.now() + totalFetches++ async function fetchAndCache () { + console.log(`[CACHE] Fetching data for key: ${key}`) const result = await fetcher(...args) cache.set(key, { data: result, createdAt: now }) + console.log(`[CACHE] Data fetched and cached for key: ${key}`) return result } const cached = cache.get(key) if (cached) { + if (cached.pendingPromise) { + console.log(`[CACHE] Waiting for pending promise for key: ${key}`) + return await cached.pendingPromise + } + const age = now - cached.createdAt if (cacheExpiry === 0 || age < cacheExpiry) { + cacheHits++ + console.log(`[CACHE] Cache hit for key: ${key}, age: ${age}ms`) return cached.data } else if (forceRefreshThreshold === 0 || age < forceRefreshThreshold) { - fetchAndCache().catch(console.error) + backgroundRefreshes++ + console.log(`[CACHE] Background refresh for key: ${key}, age: ${age}ms`) + cached.pendingPromise = fetchAndCache() + cached.pendingPromise.finally(() => { + console.log(`[CACHE] Background refresh completed for key: ${key}`) + delete cached.pendingPromise + }) return cached.data } - } else if (forceRefreshThreshold === 0) { - fetchAndCache().catch(console.error) - return null } - return await fetchAndCache() + cacheMisses++ + console.log(`[CACHE] Cache miss for key: ${key}`) + const entry = { createdAt: now, pendingPromise: fetchAndCache() } + cache.set(key, entry) + try { + entry.data = await entry.pendingPromise + return entry.data + } catch (error) { + console.error(`[CACHE] Error fetching data for key: ${key}`, error) + cache.delete(key) + throw error + } finally { + console.log(`[CACHE] Fetch completed for key: ${key}`) + delete entry.pendingPromise + } } }