mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-11-11 08:43:59 +03:00
Added implementation for event-aware cache
refs https://github.com/TryGhost/Toolbox/issues/522 - The main feature of this cache wrapper is being able to "reset" the the cache without calling the "reset" on the wrapped cache. Being able to invalidate caches without accessing the data is a feature needed to run on caches with shared environment. - Cache invalidation happens through a special "reset time" key being added to each key when setting or getting a value, when the cache is reset the reset time is set to a new value - essentially invalidating all previously accessible values.
This commit is contained in:
parent
0301f5983e
commit
f74b19ab61
@ -1,9 +1,51 @@
|
||||
class EventAwareCacheWrapper {
|
||||
#cache;
|
||||
|
||||
#lastReset;
|
||||
|
||||
/**
|
||||
* @param {Object} deps
|
||||
* @param {Object} deps.cache - cache instance extending adapter-base-cache
|
||||
* @param {Object} [deps.eventRegistry] - event registry instance
|
||||
* @param {Number} [deps.lastReset] - timestamp of last reset
|
||||
* @param {String[]} [deps.resetEvents] - event to listen to triggering reset
|
||||
*/
|
||||
constructor(deps) {
|
||||
this.#cache = deps.cache;
|
||||
this.#lastReset = deps.lastReset || Date.now();
|
||||
|
||||
if (deps.resetEvents && deps.eventRegistry) {
|
||||
this.#initListeners(deps.eventRegistry, deps.resetEvents);
|
||||
}
|
||||
}
|
||||
|
||||
#initListeners(eventRegistry, eventsToResetOn) {
|
||||
eventsToResetOn.forEach((event) => {
|
||||
eventRegistry.on(event, () => {
|
||||
this.reset();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
#buildResetAwareKey(key) {
|
||||
return `${this.#lastReset}:${key}`;
|
||||
}
|
||||
|
||||
async get(key) {
|
||||
return this.#cache.get(this.#buildResetAwareKey(key));
|
||||
}
|
||||
|
||||
async set(key, value) {
|
||||
return this.#cache.set(this.#buildResetAwareKey(key), value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the cache without removing of flushing the keys
|
||||
* The mechanism is based on adding a timestamp to the key
|
||||
* This way the cache is invalidated but the keys are still there
|
||||
*/
|
||||
reset() {
|
||||
this.#lastReset = Date.now();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,8 +1,60 @@
|
||||
const assert = require('assert');
|
||||
const InMemoryCache = require('@tryghost/adapter-cache-memory-ttl');
|
||||
|
||||
const EventAwareCacheWrapper = require('../index');
|
||||
const {EventEmitter} = require('stream');
|
||||
|
||||
const sleep = ms => (
|
||||
new Promise((resolve) => {
|
||||
setTimeout(resolve, ms);
|
||||
})
|
||||
);
|
||||
|
||||
describe('EventAwareCacheWrapper', function () {
|
||||
it('Can initialize', function () {
|
||||
assert.ok(new EventAwareCacheWrapper());
|
||||
const cache = new InMemoryCache();
|
||||
const wrappedCache = new EventAwareCacheWrapper({
|
||||
cache
|
||||
});
|
||||
assert.ok(wrappedCache);
|
||||
});
|
||||
|
||||
describe('get', function () {
|
||||
it('calls a wrapped cache with extra key', async function () {
|
||||
const cache = new InMemoryCache();
|
||||
const lastReset = Date.now();
|
||||
const wrapper = new EventAwareCacheWrapper({
|
||||
cache: cache,
|
||||
lastReset: lastReset
|
||||
});
|
||||
|
||||
await wrapper.set('a', 'b');
|
||||
assert.equal(await wrapper.get('a'), 'b');
|
||||
assert.equal(await cache.get(`${lastReset}:a`), 'b');
|
||||
});
|
||||
});
|
||||
|
||||
describe('listens to reset events', function () {
|
||||
it('resets the cache when reset event is triggered', async function () {
|
||||
const cache = new InMemoryCache();
|
||||
const lastReset = Date.now();
|
||||
const eventRegistry = new EventEmitter();
|
||||
const wrapper = new EventAwareCacheWrapper({
|
||||
cache: cache,
|
||||
lastReset: lastReset,
|
||||
resetEvents: ['site.changed'],
|
||||
eventRegistry: eventRegistry
|
||||
});
|
||||
|
||||
await wrapper.set('a', 'b');
|
||||
assert.equal(await wrapper.get('a'), 'b');
|
||||
|
||||
// let the time tick to get new lastReset
|
||||
await sleep(100);
|
||||
|
||||
eventRegistry.emit('site.changed');
|
||||
|
||||
assert.equal(await wrapper.get('a'), undefined);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user