🌳
pt0/serverF/libF/memCacheF.mts
1import * as _ from 'lodash-es'
4type CacheEntry<T> = { expAtMs: number, cacheVal: T }
6const memCacheH: Record<string, CacheEntry<unknown>> = {}
7const keysSortedByExp: string[] = []
9type CacheFetchOpts<T> = {
10 cacheKey: string
11 ttlMs: number
12 cacheMissFn: () => Promise<T>
15export const cacheFetch = async <T,>({cacheKey, ttlMs, cacheMissFn}: CacheFetchOpts<T>): Promise<T> => {
16 let now = _.now()
18 const nowIdx = _.sortedIndexBy(keysSortedByExp, 'nowidx', (ckey) => {
19 if (ckey == 'nowidx') return now
20 if (!memCacheH[ckey]) {
21 throw new PtErr('!memCacheH[ckey]', {ckey, cacheKey, memCacheH, keysSortedByExp})
22 }
23 return memCacheH[ckey].expAtMs
24 })
26 const delCount = nowIdx
27 const expiredKeys = keysSortedByExp.splice(0, delCount)
28 // console.log({delCount, expiredKeys}, _.map(keysSortedByExp, (ckey) => memCacheH[ckey].expAtMs))
29 if (expiredKeys.length > 0) {
30 console.log(delCount, 'expired: ', expiredKeys.join(' '))
31 _.each(expiredKeys, (ckey) => {
32 delete memCacheH[ckey]
33 })
34 }
36 const existingEntry = memCacheH[cacheKey]
37 if (existingEntry) {
38 return existingEntry.cacheVal as T
39 }
41 const cacheVal = await cacheMissFn()
43 const newEntry = {
44 expAtMs: now + ttlMs,
45 cacheVal
46 }
47 memCacheH[cacheKey] = newEntry
49 const sortedIdx = _.sortedIndexBy(keysSortedByExp, cacheKey, (ckey) => {
50 if (!memCacheH[ckey]) {
51 console.log('!memCacheH[ckey]', {ckey, cacheKey, memCacheH, keysSortedByExp})
52 }
53 return memCacheH[ckey].expAtMs
54 })
55 keysSortedByExp.splice(sortedIdx, 0, cacheKey)
57 return cacheVal