Last active
October 6, 2017 19:58
-
-
Save tlhunter/98e5506c457b280024b140d16a5433ee to your computer and use it in GitHub Desktop.
In-Memory Namespaced Key Expiration Cache
This file contains 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
/** | |
* 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