Javascript LRU-cache with registry for cache change events
A Javascript LRU-cache for node and/or browser, featuring:
get
and getWithoutLruChange
now take custom entry getter as optional third argument.setEntryGetter
setEntryGetter
set
)get
or getWithoutLruChange
, the given key is not in the cache and an entryGetter is set, then the entry getter will be calledget
or getWithoutLruChange
will return the same Promise, until it is resolvedentry.value
returned by the entry getterhas
getWithoutLruChange
could be used in all cases to test if a value is already cached. However, now the has
method might be mandatory to do so in case an entry getter was set.
npm install --save @swarmy/lru-cache
There are for sure more than a handful of different LRU-cache implementations flying around on npm.
What makes this one unique is that it features the use of alternate keys and an event registry for cache change events.
Alternate keys are useful in situations where certain cache consumers have no access to the primary key, or would have to construct or fetch the primary key in advance.
Cache events are useful to have a central point to handle object changes in an application with multiple models. E.g. register all models as listeners and ensure that all entity updates and deletes go over the cache. Than you can ensure that each update or delete of a certain entity, be it due to a change coming from a certain controller or due to a server push, will keep all models in sync. I have applications where I have no need to cache for performance reasons, but still use this pattern to keep my Redux state up to date (by having a single cache listener that dispatches a corresponding action).
import {getCache} from "@swarmy/lru-cache";
// If you use in nodejs and prefer the old-fashioned way:
// var getCache = require("@swarmy/lru-cache").getCache;
...
const userCache = getCache("User");
userCache.set(user.id, user);
...
let user = userCache.get(userId);
if (!user) {
user = await fetchUser(userId);
}
...
To register for all cache change events
import {registerCacheChangedHandler} from "@swarmy/lru-cache";
registerCacheChangedHandler(changeObject => {
console.log("changes:", changeObject);
});
git clone https://github.com/gneu77/lru-cache.git
cd lru-cache
npm install
// no you can do:
npm run build
npm run test
npm run test-performance
npm run generate-doc
The following 4 functions can be imported from lru-cache:
Method | Arguments | Returns | Description | |
---|---|---|---|---|
cacheTransaction |
callbackOrPromise: function\ | Promise | undefined | All cache events (set, delete, etc.) within the callback will be combined into a single event that will be dispatched when finished. |
clearAllCaches |
none | undefined | All existing cache instances will be invalidated. If dispatchClearRemoves is set true for a cache, then a single event will be dispatched for the clear. | |
getCache |
valueType: string | LruCache | Return LruCache for given value type. For the same value type, the same cache instance will be returned (LruCache is a per-value-type singleton. | |
registerCacheChangedHandler |
changedHandler: function, valueTypes: Array\ | string | handlerHandle: object | changedHandler will be called with corresponding cache event. If valueTypes is not specified or null, the handler will receive all cache events. Else it will only receive cache events for the given type(s). The returned handlerHandle has the methods unregister , deactivate , activate and isRegisterd . It also has the fields isActive and valueType |
LruCache has the following methods:
Method | Arguments | Returns | Description |
---|---|---|---|
clear |
none | undefined | Invalidate the cache. If dispatchClearRemoves is set true for the cache, then a single event will be dispatched for the clear. |
delete |
key: string | wasInCache: boolean | Remove an entry from the cache. A corresponding cache event will be dispatched. Here, no alternate key is allowed! |
dispatchClearRemoves |
newValue: boolean | undefined | Set whether a cache clear should dispatch a cache event (default: false). |
dispatchLruRemoves |
newValue: boolean | undefined | Set whether a LRU-remove (entry being removed due to exceeded cache size) should dispatch a cache event (default: false). |
forEach |
callback: function | undefined | Iterate over the cache from olodest to newest value. Callback receives cache entry as argument, being an object with key , value and alternateKeys . |
get |
keyOrAlternateKey: string, notFromCache: boolean (default: false), customEntryGetter: function (default: null) | value | Get cached value or undefined. The returned value will be made the newest value in the cache. If an entry getter is set (see setEntryGetter ), this getter will be used in case of a cache miss. If the entry getter is an async function, then a Promise will be returned that resolves to the value (Subsequent calls to get will return the same Promise until resolved, so the entry getter is called only once). If notFromCache is set true, the value will be taken from entry getter, even if a value is already cached. If in this case no entry getter was set, a corresponding error will be thrown. A custom entry getter can also be specified as optional third argument. |
getEntries |
none | Array | Returns an array with all cache entries, order from oldest to newest. |
getMaxSize |
none | Int | Returns the current max size of the cache. |
getSize |
none | Int | Returns the number of entries in the cache. |
getValueType |
none | string | Returns the value type of the cache. |
getWithoutLruChange |
keyOrAlternateKey: string, notFromCache: boolean (default: false), customEntryGetter: function (default: null) | value | Like get , but without making the entry the newest in the cache. |
has |
keyOrAlternateKey: string | isInCache: boolean | True, if the key or alternate key is in the cache. |
set |
keyValueAlternateKeys: object | undefined | Insert or update a value in the cache. The argument must be an object with key and value . Optionally it can also have alternateKeys , being string or array of strings. After set, the value will be the newest in the cache. Dispatches a corresponding cache event. If an insert exceeds the max cache size and dispatchLruRemoves is set true, it will be included in the event. |
setAll |
Array[keyValueAlternateKeys] | undefined | Like set , but for an array of cache entries. Leading to a single cache event. |
setAllAsync |
Array[keyValueAlternateKeys] | Promise | Like setAll , but performing insert and event dispatch in another event loop, resolving the returned promise afterwards. |
setAsync |
keyValueAlternateKeys: object | Promise | Like set , but performing insert and event dispatch in another event loop, resolving the returned promise afterwards. |
setEntryGetter |
entryGetter: function | undefined | Set a getter that can be used to retrieve a cache entry (keyValueAlternateKeys-object) by key in case it is not yet in the cache. For values that might be called by alternate key, the getter should also be able to handle this. |
setMaxSize |
newMaxSize: int | undefined | Change the max size of the cache (default: 500). If the new maxSize is less than the old and this leads to LRU-removes and dispatchLruRemoves is set true, then a single cache event will be dispatched. |
Here is the structure of the cache change event argument:
{
valueTypes: Set(),
<valueType>: {
inserts: [{key, value, alternateKeys, order}],
clearRemoves: [{key, value, alternateKeys, order}],
lruRemoves: [{key, value, alternateKeys, order}],
deleteRemoves: [{key}],
},
...
}
Garbage collected languages without weak references make the use of some patterns impossible. Two of these patterns affect this library: