Skip to content

Commit

Permalink
Add disposeAfter() option
Browse files Browse the repository at this point in the history
Fix #161

It turned out to not be such a perf impact after all.
  • Loading branch information
isaacs committed Feb 9, 2022
1 parent b77d8e9 commit 73b7f83
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 0 deletions.
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,13 @@ If you put more stuff in it, then items will fall out.

Optional, must be a function.

* `disposeAfter` The same as `dispose`, but called _after_ the entry is
completely removed and the cache is once again in a clean state.

It is safe to add an item right back into the cache at this point.
However, note that it is _very_ easy to inadvertently create infinite
recursion in this way.

* `noDisposeOnSet` Set to `true` to suppress calling the `dispose()`
function if the entry key is still accessible within the cache.

Expand Down
45 changes: 45 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ class LRUCache {
updateAgeOnGet,
allowStale,
dispose,
disposeAfter,
noDisposeOnSet,
maxSize,
sizeCalculation,
Expand Down Expand Up @@ -124,6 +125,13 @@ class LRUCache {
if (typeof dispose === 'function') {
this.dispose = dispose
}
if (typeof disposeAfter === 'function') {
this.disposeAfter = disposeAfter
this.disposed = []
} else {
this.disposeAfter = null
this.disposed = null
}
this.noDisposeOnSet = !!noDisposeOnSet

if (this.maxSize) {
Expand Down Expand Up @@ -367,6 +375,9 @@ class LRUCache {
if (v !== oldVal) {
if (!noDisposeOnSet) {
this.dispose(oldVal, k, 'set')
if (this.disposeAfter) {
this.disposed.push([oldVal, k, 'set'])
}
}
this.removeItemSize(index)
this.valList[index] = v
Expand All @@ -378,6 +389,12 @@ class LRUCache {
this.initializeTTLTracking()
}
this.setItemTTL(index, ttl)
if (this.disposeAfter) {
while (this.disposed.length) {
this.disposeAfter(...this.disposed.shift())
}
}
return this
}

newIndex () {
Expand Down Expand Up @@ -407,6 +424,9 @@ class LRUCache {
const k = this.keyList[head]
const v = this.valList[head]
this.dispose(v, k, 'evict')
if (this.disposeAfter) {
this.disposed.push([v, k, 'evict'])
}
this.removeItemSize(head)
this.head = this.next[head]
this.keyMap.delete(k)
Expand Down Expand Up @@ -471,15 +491,24 @@ class LRUCache {
}
}

get del () {
deprecatedMethod('del', 'Please use cache.delete() instead.')
return this.delete
}
delete (k) {
let deleted = false
if (this.size !== 0) {
const index = this.keyMap.get(k)
if (index !== undefined) {
deleted = true
if (this.size === 1) {
this.clear()
} else {
this.removeItemSize(index)
this.dispose(this.valList[index], k, 'delete')
if (this.disposeAfter) {
this.disposed.push([this.valList[index], k, 'delete'])
}
this.keyMap.delete(k)
this.keyList[index] = null
this.valList[index] = null
Expand All @@ -496,6 +525,12 @@ class LRUCache {
}
}
}
if (this.disposed) {
while (this.disposed.length) {
this.disposeAfter(...this.disposed.shift())
}
}
return deleted
}

clear () {
Expand All @@ -504,6 +539,11 @@ class LRUCache {
this.dispose(this.valList[index], this.keyList[index], 'delete')
}
}
if (this.disposeAfter) {
for (const index of this.rindexes()) {
this.disposed.push([this.valList[index], this.keyList[index], 'delete'])
}
}
this.keyMap.clear()
this.valList.fill(null)
this.keyList.fill(null)
Expand All @@ -519,6 +559,11 @@ class LRUCache {
this.free.length = 0
this.calculatedSize = 0
this.size = 0
if (this.disposed) {
while (this.disposed.length) {
this.disposeAfter(...this.disposed.shift())
}
}
}
get reset () {
deprecatedMethod('reset', 'Please use cache.clear() instead.')
Expand Down
45 changes: 45 additions & 0 deletions test/dispose.js
Original file line number Diff line number Diff line change
Expand Up @@ -139,3 +139,48 @@ t.test('noDisposeOnSet with delete()', t => {

t.end()
})

t.test('disposeAfter', t => {
const c = new LRU({
max: 5,
disposeAfter: (v, k, r) => {
if (k === 2) {
// increment it every time it gets disposed, but only one time
c.set(k, v + 1, { noDisposeOnSet: true })
}
},
})

for (let i = 0; i < 100; i++) {
c.set(i, i)
}
t.same([...c.entries()], [
[ 99, 99 ],
[ 98, 98 ],
[ 2, 21 ],
[ 97, 97 ],
[ 96, 96 ],
])
c.delete(2)
t.same([...c.entries()], [
[ 2, 22 ],
[ 99, 99 ],
[ 98, 98 ],
[ 97, 97 ],
[ 96, 96 ],
])
for (let i = 96; i < 100; i++) {
c.set(i, i+1)
}
t.same([...c.entries()], [
[ 99, 100 ],
[ 98, 99 ],
[ 97, 98 ],
[ 96, 97 ],
[ 2, 22 ],
])
c.clear()
t.same([...c.entries()], [[2, 23]])

t.end()
})

0 comments on commit 73b7f83

Please sign in to comment.