Build a cache that forgets
Five steps that turn a plain object into a cache with expiry, a size cap and least-recently-used eviction.
What you end up with
A cache a real service could use: entries that expire, a hard size limit, least-recently-used eviction, and hit and miss counts that show whether it is earning its keep.
- 5 steps
- 20 tests
- ≈25 minutes
- javascript
- 1
Remember a value
cacheSet()Complete
cacheSet(cache, key, value, now).cacheis{ ttlMs, maxEntries, entries }. Store{ value, storedAt: now, usedAt: now }underkeyincache.entries, replacing anything already there, and return the cache. - 2
Stop serving stale answers
cacheGet()Complete
cacheGet(cache, key, now). Returnnullifkeyis not incache.entries, or ifnow - storedAtis at leastcache.ttlMs— in that case delete the entry first. Otherwise set the entry’susedAttonowand return its value. - 3
Cap how much it holds
cachePut()Complete
cachePut(cache, key, value, now). Store the entry withcacheSet, then whilecache.entriesholds more thancache.maxEntrieskeys, delete the entry with the smalleststoredAt. Return the cache. - 4
Evict the one nobody wants
evictLru()Complete
evictLru(cache). Whilecache.entriesholds more thancache.maxEntrieskeys, delete the entry with the smallestusedAt; if two tie, the key added first is the one that goes. Return the cache. Then changecachePutto callevictLruinstead of dropping the oldest entry. - 5
Count the hits and misses
runCache()Complete
runCache(cache, ops). Each op is["set", key, value, now]or["get", key, now]; run sets throughcachePutand gets throughcacheGet, collecting each get’s return value in order. Return{ results, hits, misses, hitRate, keys }, where a get returningnullis a miss,hitRateis hits divided by the number of gets rounded to two decimals (0when there were no gets), andkeysisObject.keys(cache.entries).