Kent C. Dodds
More than you knew you need to know about caching
Your brain needs this π§
Can't delete it. Can't reduce it. Can't "make it fast."
π€ Cash it! π€
π₯΄ Cache it! π₯΄
In computing, a cache is a hardware or software component that stores data so that future requests for that data can be served faster; the data stored in a cache might be the result of an earlier computation or a copy of data stored elsewhere. A cache hit occurs when the requested data can be found in a cache, while a cache miss occurs when it cannot. Cache hits are served by reading data from the cache, which is faster than recomputing a result or reading from a slower data store; thus, the more requests that can be served from the cache, the faster the system performs.
function computePi() {
let pi = 0
let sign = 1
for (let i = 0; i < 1000000; i++) {
const term = sign / (2 * i + 1)
pi += term
sign *= -1
}
return Math.round(pi * 4e10) / 1e10
}
let pi
function computePiCached() {
if (typeof pi === 'undefined') {
pi = computePi()
}
return pi
}
Store the result of a computation somewhere and return that stored value instead of recomputing it again.
function computePi(precision: number) {
let pi = 0
let sign = 1
for (let i = 0; i < 1000000; i++) {
pi += sign / (2 * i + 1)
sign *= -1
}
const factor = 10 ** precision
return Math.round(pi * 4 * factor) / factor
}
const piCache = new Map<number, number>()
function computePiCached(precision: number) {
if (!piCache.has(precision)) {
piCache.set(precision, computePi(precision))
}
return piCache.get(precision)
}
π Cache Keys!!
function sum(a: number, b: number) {
return a + b
}
const sumCache = new Map<string, number>()
function sumCached(a: number, b: number) {
const key = `${a},${b}`
if (!sumCache.has(key)) {
sumCache.set(key, sum(a, b))
}
return sumCache.get(key)
}
sumCached(1, 2) // cache miss: 3
sumCached(1, 2) // cache hit: 3
function addDays(count: number) {
const millisecondsInDay = 1000 * 60 * 60 * 24
return new Date(Date.now() + count * millisecondsInDay)
}
const cache = new Map<string, Date>()
function addDaysCached(count: number) {
const key = `add-days:${count}`
if (!cache.has(key)) {
cache.set(key, addDays(count))
}
return cache.get(key)
}
find the bug π
π
addDaysCached(3) // cache miss: 3 days from today
addDaysCached(3) // cache hit: 3 days from today
// ... wait 24 hours...
addDaysCached(3) // cache hit: 3 days from yesterday π±
// That's 2 days from today!
The cache key must* account for all inputs required to determine the result
useMemo(() => {
// ...
}, [/* ... ugh... */])
import fs from 'fs'
function getVideoBuffer(filepath: string) {
return fs.promises.readFile(filepath)
}
const videoBufferCache = new Map<string, Buffer>()
async function getVideoBufferCached(filepath: string) {
if (!videoBufferCache.has(filepath)) {
videoBufferCache.set(filepath, await getVideoBuffer(filepath))
}
return videoBufferCache.get(filepath)
}
find the bug π
π
FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory
1: 00007FF6E7A84E05 node::Abort+21
2: 00007FF6E7A84F49 node::OnFatalError+297
3: 00007FF6E7A84D2F node::OnFatalError+31
4: 00007FF6E7A84D17 node::OnFatalError+23
5: 00007FF6E812C7F8 v8::Utils::ReportOOMFailure+184
...
Note: cache size can still get out of control, so keep an eye out!
Kinda like DataLoader