UNPKG

transitory

Version:

In-memory cache with high hit rates via LFU eviction. Supports time-based expiration, automatic loading and metrics.

293 lines 10.2 kB
"use strict"; var __extends = (this && this.__extends) || (function () { var extendStatics = function (d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; return extendStatics(d, b); }; return function (d, b) { if (typeof b !== "function" && b !== null) throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; })(); var __values = (this && this.__values) || function(o) { var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; if (m) return m.call(o); if (o && typeof o.length === "number") return { next: function () { if (o && i >= o.length) o = void 0; return { value: o && o[i++], done: !o }; } }; throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); }; var __read = (this && this.__read) || function (o, n) { var m = typeof Symbol === "function" && o[Symbol.iterator]; if (!m) return o; var i = m.call(o), r, ar = [], e; try { while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); } catch (error) { e = { error: error }; } finally { try { if (r && !r.done && (m = i["return"])) m.call(i); } finally { if (e) throw e.error; } } return ar; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.BoundlessCache = void 0; var AbstractCache_1 = require("../AbstractCache"); var RemovalReason_1 = require("../RemovalReason"); var symbols_1 = require("../symbols"); var DATA = Symbol('boundlessData'); var EVICTION_DELAY = 5000; /** * Boundless cache. */ var BoundlessCache = /** @class */ (function (_super) { __extends(BoundlessCache, _super); function BoundlessCache(options) { var _this = _super.call(this) || this; _this[DATA] = { values: new Map(), removalListener: options.removalListener || null, evictionTimeout: null }; return _this; } Object.defineProperty(BoundlessCache.prototype, "maxSize", { /** * The maximum size the cache can be. Will be -1 if the cache is unbounded. * * @returns * maximum size, always `-1` */ get: function () { return -1; }, enumerable: false, configurable: true }); Object.defineProperty(BoundlessCache.prototype, "size", { /** * The current size of the cache. * * @returns * entries in the cache */ get: function () { return this[DATA].values.size; }, enumerable: false, configurable: true }); Object.defineProperty(BoundlessCache.prototype, "weightedSize", { /** * The size of the cache weighted via the activate estimator. * * @returns * entries in the cache */ get: function () { return this.size; }, enumerable: false, configurable: true }); /** * Store a value tied to the specified key. Returns the previous value or * `null` if no value currently exists for the given key. * * @param key - * key to store value under * @param value - * value to store * @returns * current value or `null` */ BoundlessCache.prototype.set = function (key, value) { var _this = this; var data = this[DATA]; var old = data.values.get(key); // Update with the new value data.values.set(key, value); // Schedule an eviction if (!data.evictionTimeout) { data.evictionTimeout = setTimeout(function () { return _this[symbols_1.MAINTENANCE](); }, EVICTION_DELAY); } // Return the value we replaced if (old !== undefined) { this[symbols_1.TRIGGER_REMOVE](key, old, RemovalReason_1.RemovalReason.REPLACED); return old; } else { return null; } }; /** * Get the cached value for the specified key if it exists. Will return * the value or `null` if no cached value exist. Updates the usage of the * key. * * @param key - * key to get * @returns * current value or `null` */ BoundlessCache.prototype.getIfPresent = function (key) { var data = this[DATA]; var value = data.values.get(key); return value === undefined ? null : value; }; /** * Peek to see if a key is present without updating the usage of the * key. Returns the value associated with the key or `null` if the key * is not present. * * In many cases `has(key)` is a better option to see if a key is present. * * @param key - * the key to check * @returns * value associated with key or `null` */ BoundlessCache.prototype.peek = function (key) { var data = this[DATA]; var value = data.values.get(key); return value === undefined ? null : value; }; /** * Delete a value in the cache. Returns the deleted value or `null` if * there was no value associated with the key in the cache. * * @param key - * the key to delete * @returns * deleted value or `null` */ BoundlessCache.prototype.delete = function (key) { var _this = this; var data = this[DATA]; var old = data.values.get(key); data.values.delete(key); if (old !== undefined) { // Trigger removal events this[symbols_1.TRIGGER_REMOVE](key, old, RemovalReason_1.RemovalReason.EXPLICIT); // Queue an eviction event if one is not set if (!data.evictionTimeout) { data.evictionTimeout = setTimeout(function () { return _this[symbols_1.MAINTENANCE](); }, EVICTION_DELAY); } return old; } else { return null; } }; /** * Check if the given key exists in the cache. * * @param key - * key to check * @returns * `true` if value currently exists, `false` otherwise */ BoundlessCache.prototype.has = function (key) { var data = this[DATA]; return data.values.has(key); }; /** * Clear all of the cached data. */ BoundlessCache.prototype.clear = function () { var e_1, _a; var data = this[DATA]; var oldValues = data.values; // Simply replace the value map new data data.values = new Map(); try { // Trigger removal events for all of the content in the cache for (var _b = __values(oldValues.entries()), _c = _b.next(); !_c.done; _c = _b.next()) { var _d = __read(_c.value, 2), key = _d[0], value = _d[1]; this[symbols_1.TRIGGER_REMOVE](key, value, RemovalReason_1.RemovalReason.EXPLICIT); } } catch (e_1_1) { e_1 = { error: e_1_1 }; } finally { try { if (_c && !_c.done && (_a = _b.return)) _a.call(_b); } finally { if (e_1) throw e_1.error; } } }; /** * Get all of the keys in the cache as an array. Can be used to iterate * over all of the values in the cache, but be sure to protect against * values being removed during iteration due to time-based expiration if * used. * * @returns * snapshot of keys */ BoundlessCache.prototype.keys = function () { this[symbols_1.MAINTENANCE](); return Array.from(this[DATA].values.keys()); }; /** * Request clean up of the cache by removing expired entries and * old data. Clean up is done automatically a short time after sets and * deletes, but if your cache uses time-based expiration and has very * sporadic updates it might be a good idea to call `cleanUp()` at times. * * A good starting point would be to call `cleanUp()` in a `setInterval` * with a delay of at least a few minutes. */ BoundlessCache.prototype.cleanUp = function () { // Simply request eviction so extra layers can handle this this[symbols_1.MAINTENANCE](); }; Object.defineProperty(BoundlessCache.prototype, "metrics", { /** * Get metrics for this cache. Returns an object with the keys `hits`, * `misses` and `hitRate`. For caches that do not have metrics enabled * trying to access metrics will throw an error. */ get: function () { throw new Error('Metrics are not supported by this cache'); }, enumerable: false, configurable: true }); BoundlessCache.prototype[symbols_1.TRIGGER_REMOVE] = function (key, value, reason) { var data = this[DATA]; // Trigger any extended remove listeners var onRemove = this[symbols_1.ON_REMOVE]; if (onRemove) { onRemove(key, value, reason); } if (data.removalListener) { data.removalListener(key, value, reason); } }; BoundlessCache.prototype[symbols_1.MAINTENANCE] = function () { // Trigger the onEvict listener if one exists var onEvict = this[symbols_1.ON_MAINTENANCE]; if (onEvict) { onEvict(); } var data = this[DATA]; if (data.evictionTimeout) { clearTimeout(data.evictionTimeout); data.evictionTimeout = null; } }; return BoundlessCache; }(AbstractCache_1.AbstractCache)); exports.BoundlessCache = BoundlessCache; //# sourceMappingURL=BoundlessCache.js.map