Skip to content

Commit

Permalink
Node 12 fixes (#16)
Browse files Browse the repository at this point in the history
- Tweak `_refresh` logic so tests pass in node 12
- Add `.eslintrc` so IDEs can show linting errors
- Fix mismatching dependencies
- Fix security audit failures
  • Loading branch information
jpage-godaddy authored Apr 27, 2020
1 parent 01755c8 commit 9628fa2
Show file tree
Hide file tree
Showing 4 changed files with 305 additions and 541 deletions.
4 changes: 4 additions & 0 deletions .eslintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"root": true,
"extends": "godaddy"
}
81 changes: 47 additions & 34 deletions lib/multi-level.js
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,8 @@ class MultiLevelCache {
}

/**
* Refresh the cache for a given key value pair
* Refresh the cache for a given key value pair. Pending refreshes are reused.
*
* @param {String} key cache key
* @param {JSONSerializable} staleItem cache value
* @param {UpdateFn} updateFn async function that defines how to get a new value
Expand All @@ -135,44 +136,56 @@ class MultiLevelCache {
*/
async _refresh(key, staleItem, updateFn, opts = {}) {
if (!(key in this._pendingRefreshes)) {
try {
const task = updateFn(key, staleItem && staleItem.value);
this._pendingRefreshes[key] = task;

const value = await task;
const cacheItem = {
value,
expiry: Date.now() + getValue(opts, 'maxAge', this._maxAge)
};

const shouldCache = getValue(opts, 'shouldCache', this.shouldCache);
if (typeof shouldCache !== 'function') {
throw new TypeError('shouldCache has to be a function');
}

// Given that we are not ignoring this value, perform an out-of-band cache update
if (shouldCache(cacheItem.value)) {
// NB: an in-band update would `await` this Promise.all block
Promise.all(this._caches.map(cache => cache.set(key, cacheItem))).catch(err => {
throw new Error(`Error caching ${key}`, err);
}).then(() => {
delete this._pendingRefreshes[key];
});
} else {
// just delete this right away because we're never caching the item
delete this._pendingRefreshes[key];
}

return { value, fromCache: false };
} catch (err) {
delete this._pendingRefreshes[key];
throw err;
}
this._pendingRefreshes[key] = this._forceRefresh(key, staleItem, updateFn, opts);
}

const value = await this._pendingRefreshes[key];
return { value, fromCache: false };
}

/**
* Refresh the cache for a given key value pair. Pending refreshes are ignored.
*
* @param {String} key cache key
* @param {JSONSerializable} staleItem cache value
* @param {UpdateFn} updateFn async function that defines how to get a new value
* @param {Object} opts An options object
* @param {ShouldCache} [opts.shouldCache] Function that determines whether or not we should cache the item
* @param {Number} [opts.maxAge] The duration, in milliseconds, before a cached item expires
*
* @private
* @async
* @returns {Promise<any>} a promise that resolves once we have refreshed the correct key
*/
async _forceRefresh(key, staleItem, updateFn, opts) {
try {
const value = await updateFn(key, staleItem && staleItem.value);
const cacheItem = {
value,
expiry: Date.now() + getValue(opts, 'maxAge', this._maxAge)
};
const shouldCache = getValue(opts, 'shouldCache', this.shouldCache);
if (typeof shouldCache !== 'function') {
throw new TypeError('shouldCache has to be a function');
}
// Given that we are not ignoring this value, perform an out-of-band cache update
if (shouldCache(cacheItem.value)) {
// NB: an in-band update would `await` this Promise.all block
Promise.all(this._caches.map(cache => cache.set(key, cacheItem))).catch(err => {
throw new Error(`Error caching ${key}`, err);
}).then(() => {
delete this._pendingRefreshes[key];
});
} else {
// just delete this right away because we're never caching the item
delete this._pendingRefreshes[key];
}
return value;
} catch (err) {
delete this._pendingRefreshes[key];
throw err;
}
}
}

module.exports = MultiLevelCache;
Loading

0 comments on commit 9628fa2

Please sign in to comment.