don't let pending cache to build up
This commit is contained in:
parent
8c0cafa3ec
commit
449568e3a2
53
lib/fetch.js
53
lib/fetch.js
@ -38,33 +38,74 @@ class LRUCache {
|
|||||||
|
|
||||||
export function cachedFetcher (fetcher, { maxSize = 100, cacheExpiry, forceRefreshThreshold, keyGenerator }) {
|
export function cachedFetcher (fetcher, { maxSize = 100, cacheExpiry, forceRefreshThreshold, keyGenerator }) {
|
||||||
const cache = new LRUCache(maxSize)
|
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) {
|
return async function cachedFetch (...args) {
|
||||||
const key = keyGenerator ? keyGenerator(...args) : JSON.stringify(args)
|
const key = keyGenerator(...args)
|
||||||
const now = Date.now()
|
const now = Date.now()
|
||||||
|
totalFetches++
|
||||||
|
|
||||||
async function fetchAndCache () {
|
async function fetchAndCache () {
|
||||||
|
console.log(`[CACHE] Fetching data for key: ${key}`)
|
||||||
const result = await fetcher(...args)
|
const result = await fetcher(...args)
|
||||||
cache.set(key, { data: result, createdAt: now })
|
cache.set(key, { data: result, createdAt: now })
|
||||||
|
console.log(`[CACHE] Data fetched and cached for key: ${key}`)
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
const cached = cache.get(key)
|
const cached = cache.get(key)
|
||||||
|
|
||||||
if (cached) {
|
if (cached) {
|
||||||
|
if (cached.pendingPromise) {
|
||||||
|
console.log(`[CACHE] Waiting for pending promise for key: ${key}`)
|
||||||
|
return await cached.pendingPromise
|
||||||
|
}
|
||||||
|
|
||||||
const age = now - cached.createdAt
|
const age = now - cached.createdAt
|
||||||
|
|
||||||
if (cacheExpiry === 0 || age < cacheExpiry) {
|
if (cacheExpiry === 0 || age < cacheExpiry) {
|
||||||
|
cacheHits++
|
||||||
|
console.log(`[CACHE] Cache hit for key: ${key}, age: ${age}ms`)
|
||||||
return cached.data
|
return cached.data
|
||||||
} else if (forceRefreshThreshold === 0 || age < forceRefreshThreshold) {
|
} 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
|
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user