@squeep/cache
v1.0.0
Published
Some caching structures, ARC and LRU, with support for multiple keys per entry, and multiple indices per cache.
Maintainers
Readme
@squeep/cache
Some naïve cache structures, with esoteric embellishments. Currently provides Least Recently Used (LRU) and Adaptive Replacement (ARC) caches.
These were originally designed for caching database records having multiple 1:1 indices, as well as m:1 indices; exemplorum gratia, two unique indentifier columns, or a record having multiple values for a column via a join.
Frills:
- A single value may be stored under multiple keys at once, by providing an array of key values.
- Additional separate key indices may be named. Keys for these additional indicies are typically derived by callback functions given the value data upon insertion. When storing the absence of a value, the callbacks are not invoked, so the relevant keys must be provided.
- Indices are
Maps by default, but any Map-like class can be specified, per index. - Statistics events of dubious-usefulness are emitted for hits, misses, and evictions.
Caveats:
- Cannot use
undefinedas any actual key. - No wall-clock expirey.
get()returnsnullfor no entry, be aware if you are storing literalnullvalues. Preferlookup()for clarity.- If you need to use an Array as a key for some reason, it needs to be set inside another array m:1 style, otherwise each array value is indexed as a key.
Common API Overview
constructor({ capacity })
See jsdoc for all available options.set(key, value, namedKeys)
Add or updatekeywithvalue.lookup(key, indexName)
Returns{ isInIndex, value }.has(key, indexName)
Shorthand forisInIndexfromlookup.get(key, indexName)
Shorthand forvaluefromlookup.delete(key, indexName)
Remove value referenced bykey.clear()
Remove all values.shrink(by)
Reduce capacity.grow(by)
Increase capacity.
LRU – Least Recently Used
A temporally-sorted, size-limited queue.
Example
const { LRU } = require('@squeep/cache');
const { log } = console;
const lru = new LRU({
id: 'my-lru',
capacity: 2,
namedIndexers: {
altId: (entry) => entry.altId,
},
});
lru.on('hit', (info) => log('hit:', info));
lru.on('miss', (info) => log('miss:', info));
lru.on('evict', (info) => log('evict:', info));
log(lru.has('key1')); // -> false
// miss: { id: 'my-lru', key: 'key1' }
lru.set('key1', { data: 'data1', altId: 1n });
log(lru.has('key1')); // -> true
// hit: { id: 'my-lru', namedKeys: { altId: 1n }, key: 'key1', count: 1, age: 1 }
log(lru.has(1n, 'altId')); // -> true
// hit: { id: 'my-lru', indexName: 'altId', namedKeys: { altId: 1n }, key: 'key1', count: 2, age: 2 }
log(lru.get(1n, 'altId')); // -> { data: 'data1', altId: 1n }
lru.set('key2', { data: 'data2', altId: 2n });
lru.set('key3', { data: 'data3', altId: [3n, 33n, 333n] });
// evict: { id: 'my-lru', key: 'key1', count: 1, age: 1 }
log(lru.has('key1')); // -> false
// miss: { id: 'my-lru', key: 'key1' }
log(lru.get('key1')); // -> null
log(lru.has(33n, 'altId')); // -> true
// hit: { id: 'my-lru', indexName: 'altId', namedKeys: { altId: [ 3n, 33n, 333n ] }, key: 33n, count: 1, age: 2 }
log(lru.size); // -> 2
log(lru.delete(333n, 'altId')); // -> true
log(lru.delete('blah')); // -> false
log(lru.size); // -> 1
lru.clear();
log(lru.size); // -> 0ARC – Adaptive Replacement Cache
Four LRUs in a trenchcoat.
Example
const { ARC } = require('@squeep/cache');
const { log } = console;
const arc = new ARC({
id: 'my-arc',
capacity: 4,
namedIndexers: {
altId: (entry) => entry.altId,
},
});
arc.on('hit', (info) => log('hit:', info));
arc.on('miss', (info) => log('miss:', info));
arc.on('evict', (info) => log('evict:', info));
log(arc.has('key1')); // -> false
// miss: { id: 'my-arc', key: 'key1' }
arc.set('key1', { data: 'data1', altId: 1n });
log(arc.has('key1')); // -> true
// hit: { id: 'my-arc', key: 'key1', count: 1, age: 2, isGhost: false }
log(arc.has(1n, 'altId')); // -> true
// hit: { id: 'my-arc', key: 'key1', indexName: 'altId', count: 2, age: 3, isGhost: false }
log(arc.get('key1')); // -> { data: 'data1', altId: 1n }
// hit: { id: 'my-arc', key: 'key1', count: 3, age: 4, isGhost: false }
arc.set('key2', { data: 'data2', altId: 2n });
arc.set('key3', { data: 'data3', altId: 3n });
arc.set('key4', { data: 'data4', altId: 4n });
// evict: { id: 'my-arc', from: 'my-arc-recent-ghost', key: 'key2', namedKeys: { altId: 2n }, count: 0, age: 4 }
log(arc.get('key2')); // -> null
// miss: { id: 'my-arc', key: 'key2' }
log(arc.size); // -> 3
arc.delete('key3');
log(arc.size); // -> 2
arc.clear();
log(arc.size); // -> 0