Skip to content

Instantly share code, notes, and snippets.

@tlhunter
Last active October 6, 2017 19:58
Show Gist options
  • Save tlhunter/98e5506c457b280024b140d16a5433ee to your computer and use it in GitHub Desktop.
Save tlhunter/98e5506c457b280024b140d16a5433ee to your computer and use it in GitHub Desktop.
In-Memory Namespaced Key Expiration Cache
/**
* In-Memory Namespaced Key Expiration Cache
*
* Assumptions: There's a finite amount of namespaces
* Data is frequently read
* Expirations don't happen frequently
* Self-cleaning, doesn't use setTimeout / setImmediate
*/
class SimpleCache {
constructor() {
this._cache = new Map();
}
/**
* Adds a new entry to the cache
*
* @param {string} namespace
* @param {string} entry
* @param {number} timeout
*/
add(namespace, entry, timeout) {
if (!this._cache.has(namespace)) {
this._cache.set(namespace, new Set());
}
let collection = this._cache.get(namespace);
let found = false;
let purge = new Set();
let now = Date.now();
collection.forEach(item => {
let [value, expire] = item;
if (value === entry) {
// Already Exists, update existing item
found = true;
item[1] = timeout + now;
}
if (expire <= now) {
purge.add(item);
return;
}
});
purge.forEach(item => collection.delete(item));
if (!found) {
// Doesn't exist, add new item
collection.add([entry, timeout + now]);
}
}
/**
* Checks to see if an entry exists in the cache
*
* @param {string} namespace
* @param {string} entry
* @return boolean
*/
has(namespace, entry) {
if (!this._cache.has(namespace)) {
return false;
}
let collection = this._cache.get(namespace);
let found = false;
let purge = new Set();
let now = Date.now();
collection.forEach(item => {
let [value, expire] = item;
if (expire <= now) {
purge.add(item);
return;
}
if (value === entry) {
found = true;
}
});
purge.forEach(item => collection.delete(item));
return found;
}
/**
* Gets a simple list of entries in the cache, no expiration data
*
* @param {string} namespace
* @return Set
*/
get(namespace) {
let items = new Set();
if (!this._cache.has(namespace)) {
return items;
}
let collection = this._cache.get(namespace)
let now = Date.now();
let purge = new Set();
collection.forEach(item => {
let [value, expire] = item;
if (expire <= now) {
purge.add(item);
return;
}
items.add(value);
});
purge.forEach(item => collection.delete(item));
return items;
}
/**
* Deletes an entry from the cache
*
* @param {string} namespace
* @param {string} entry
* @return Number
*/
delete(namespace, entry) {
if (!this._cache.has(namespace)) {
return false;
}
let collection = this._cache.get(namespace);
let found = false;
let purge = new Set();
let now = Date.now();
collection.forEach(item => {
let [value, expire] = item;
if (expire <= now) {
purge.add(item);
return;
}
if (value === entry) {
found = true;
purge.add(item);
}
});
purge.forEach(item => collection.delete(item));
return found;
}
/**
* Returns a simple representation to be converted to JSON
* e.g. x = new SimpleCache(); x.add('a', 'b', 100); JSON.stringify(x, null, 2);
* @return Object
*/
toJSON() {
let json = {};
this._cache.forEach((value, key) => {
json[key] = [];
value.forEach((item) => {
json[key].push({value: item[0], expire: item[1]});
});
});
return json;
}
}
module.exports = SimpleCache;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment