Last active
September 10, 2020 11:39
-
-
Save NikhilVerma/eeb941a282c0113e092e34e54475afaf to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Simple implementation of LRU + SWR cache approaches to enable fast cache retrieval at the same time allowing refresh in the background | |
*/ | |
import LRUCache from 'lru-cache'; | |
const lruCache = new LRUCache<string, CacheFormat<any>>({ | |
max: 1000, | |
maxAge: Infinity, | |
// These two options are reduntant since we cache forever, but if we decide to change it | |
// they will be helpful | |
stale: true, | |
updateAgeOnGet: true, | |
}); | |
type CacheFormat<T> = { | |
cacheAge: number; | |
data: T; | |
}; | |
const IN_PROGRESS: Record<string, Promise<any> | null> = {}; | |
export type GetSwrProps<T> = { | |
key: object | string; | |
timeout?: number; | |
fetch: () => Promise<T>; | |
}; | |
/** | |
* Returns a stale version of item while it refreshes cached entries in the background | |
*/ | |
export async function getStaleWhileRefresh<T>({ | |
key, | |
fetch, | |
timeout = 1000 * 10, | |
}: GetSwrProps<T>): Promise<T> { | |
const cacheKey = JSON.stringify(key, null, 0); | |
const cachedItem = lruCache.get(cacheKey); | |
// Prevent multiple requests | |
if (!cachedItem && IN_PROGRESS[cacheKey]) { | |
return IN_PROGRESS[cacheKey]; | |
} | |
if (cachedItem) { | |
// We hit a cached item, refresh it if beyond cache age | |
if (Date.now() - (cachedItem.cacheAge + timeout) > 0) { | |
refreshCache(); | |
} | |
return cachedItem.data; | |
} | |
refreshCache(); | |
function fetchAndSet() { | |
const now = Date.now(); | |
return fetch() | |
.then((data) => { | |
lruCache.set(cacheKey, { | |
cacheAge: Date.now(), | |
data, | |
}); | |
IN_PROGRESS[cacheKey] = null; | |
delete IN_PROGRESS[cacheKey]; | |
console.log( | |
'[swr-cache] Refreshed cache for', | |
cacheKey, | |
'time taken', | |
`${Date.now() - now}ms` | |
); | |
return data; | |
}) | |
.catch((err) => { | |
IN_PROGRESS[cacheKey] = null; | |
delete IN_PROGRESS[cacheKey]; | |
console.error(`[swr-cache] Failed to update cache for ${cacheKey}`, err); | |
throw err; | |
}); | |
} | |
function refreshCache() { | |
// If we don't have a fetch already in progress then do one | |
if (!IN_PROGRESS[cacheKey]) { | |
IN_PROGRESS[cacheKey] = fetchAndSet(); | |
} | |
} | |
return IN_PROGRESS[cacheKey]; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment