Last active
February 15, 2018 09:08
-
-
Save slonoed/55e790c28d6e62633e5b2885eaa88372 to your computer and use it in GitHub Desktop.
Simple memory cache
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Simple implementation of memory cache | |
* | |
* Is use async API in case we replace it with redis. | |
* | |
* Keys should be symbols. | |
*/ | |
const DEFAULT_TTL = 5 * 60 * 1000 | |
const CLEAN_INTERVAL = DEFAULT_TTL * 1.5 | |
export default class Cache { | |
constructor(options = {}) { | |
this.ttl = options.ttl || DEFAULT_TTL | |
this.storage = new Map() | |
this.activeGetters = new Map() | |
if (process.env.NODE_ENV === 'production') { | |
setInterval(() => { | |
for (const [key, item] of this.storage) { | |
if (item && item.expiredAt < Date.now()) { | |
this.storage.delete(key) | |
} | |
} | |
}, CLEAN_INTERVAL) | |
} | |
} | |
async set(key, value, ttl) { | |
if (typeof key !== 'symbol') { | |
throw new Error('Key should be Symbol') | |
} | |
const item = { | |
expiredAt: Date.now() + (ttl || this.ttl), | |
value, | |
} | |
this.storage.set(key, item) | |
} | |
async get(key) { | |
if (typeof key !== 'symbol') { | |
throw new Error('Key should be Symbol') | |
} | |
const item = this.storage.get(key) | |
if (!item || item.expiredAt < Date.now()) { | |
return null | |
} | |
return item.value | |
} | |
// Return value from cache. If no value in cache, call getter | |
// write result to cache and return. If previous getter still running | |
// wait for it and return result. | |
async getOrUpdate(key, getter, ttl) { | |
if (typeof key !== 'symbol') { | |
throw new Error('Key should be Symbol') | |
} | |
const item = this.storage.get(key) | |
if (item && item.expiredAt >= Date.now()) { | |
return item.value | |
} | |
const activeGetter = this.activeGetters.get(key) | |
if (activeGetter) { | |
return activeGetter | |
} | |
const currentGetter = getter() | |
this.activeGetters.set(key, currentGetter) | |
let value | |
try { | |
value = await currentGetter | |
await this.set(key, value, ttl) | |
} finally { | |
this.activeGetters.delete(key) | |
} | |
return value | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment