Skip to content

Commit fcb797e

Browse files
committed
Call dispose() when fetch overwrites a value
This corrects an oversight in the disposal flow on set overwrites. We don't call dispose when creating the BackgroundFetch, because of course, the thing hasn't been replaced yet. And we don't call dispose when _replacing_ a BackgroundFetch, because that isn't a value, it's just a potential value, so we abort the fetch and forget about it (which is a no-op if the set() in question is the resolution of that BackgroundFetch). The missing piece is that we *do* have to dispose() the `__staleWhileFetching` value on that BackgroundFetch promise, if there is one, when overwriting a BackgroundFetch. Fix: #309
1 parent 0c63b0d commit fcb797e

File tree

2 files changed

+33
-2
lines changed

2 files changed

+33
-2
lines changed

src/index.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1682,6 +1682,15 @@ export class LRUCache<K extends {}, V extends {}, FC = unknown> {
16821682
if (v !== oldVal) {
16831683
if (this.#hasFetchMethod && this.#isBackgroundFetch(oldVal)) {
16841684
oldVal.__abortController.abort(new Error('replaced'))
1685+
const { __staleWhileFetching: s } = oldVal
1686+
if (s !== undefined && !noDisposeOnSet) {
1687+
if (this.#hasDispose) {
1688+
this.#dispose?.(s as V, k, 'set')
1689+
}
1690+
if (this.#hasDisposeAfter) {
1691+
this.#disposed?.push([s as V, k, 'set'])
1692+
}
1693+
}
16851694
} else if (!noDisposeOnSet) {
16861695
if (this.#hasDispose) {
16871696
this.#dispose?.(oldVal as V, k, 'set')

test/fetch.ts

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ if (typeof performance === 'undefined') {
22
global.performance = require('perf_hooks').performance
33
}
44
import t from 'tap'
5-
import { LRUCache, BackgroundFetch } from '../'
5+
import { BackgroundFetch, LRUCache } from '../'
66
import { expose } from './fixtures/expose'
77

88
const fn: LRUCache.Fetcher<any, any> = async (_, v) =>
@@ -720,7 +720,10 @@ t.test('allowStaleOnFetchAbort', async t => {
720720
const p = c.fetch(1, { signal: ac.signal })
721721
ac.abort(new Error('gimme the stale value'))
722722
t.equal(await p, 10)
723-
t.equal(c.get(1, { allowStale: true, noDeleteOnStaleGet: true }), 10)
723+
t.equal(
724+
c.get(1, { allowStale: true, noDeleteOnStaleGet: true }),
725+
10
726+
)
724727
const p2 = c.fetch(1)
725728
c.set(1, 100)
726729
t.equal(await p2, 10)
@@ -844,3 +847,22 @@ t.test('has false for pending fetch without stale val', async t => {
844847
t.equal(c.has(1), true)
845848
}
846849
})
850+
851+
t.test('properly dispose when using fetch', async t => {
852+
const disposes: [number, number, string][] = []
853+
const disposeAfters: [number, number, string][] = []
854+
let i = 0
855+
const c = new LRUCache<number, number>({
856+
max: 3,
857+
ttl: 10,
858+
dispose: (key, val, reason) => disposes.push([key, val, reason]),
859+
disposeAfter: (key, val, reason) =>
860+
disposeAfters.push([key, val, reason]),
861+
fetchMethod: async () => Promise.resolve(i++),
862+
})
863+
t.equal(await c.fetch(1), 0)
864+
clock.advance(20)
865+
t.equal(await c.fetch(1), 1)
866+
t.strictSame(disposes, [[0, 1, 'set']])
867+
t.strictSame(disposeAfters, [[0, 1, 'set']])
868+
})

0 commit comments

Comments
 (0)