doublesharp/lru-cache-for-clusters-as-promised

View on GitHub
lru-cache-for-clusters-as-promised.js

Summary

Maintainability
A
3 hrs
Test Coverage
A
100%
/**
 * Provide a cluster-optimized lru-cache with Promises
 *
 * @module lru-cache-for-clusters-as-promised
 * @exports LRUCacheForClustersAsPromised
 */

'use strict';

const cluster = require('cluster');
const master = require('./lib/master');
const worker = require('./lib/worker');
const utils = require('./lib/utils');

/**
 * LRUCacheForClustersAsPromised roughly approximates the functionality of LRUCache
 * but in a promisified way. When running as a cluster workers send requests to the
 * master thread which actually holds the cache via IPC which then sends a response
 * that resolves the Promise. For non-clustered environments a Promisfied interface
 * to the cache is provided to match the interface for clustered environments.
 *
 * @param {Object} options The lru-cache options. Properties can be set, functions cannot.
 * @return {Object} Object with LRU methods
 */
class LRUCacheForClustersAsPromised {
  constructor(options = {}) {
    // this is how the clustered cache differentiates
    this.namespace = options.namespace || 'default';

    // this is how long the worker will wait for a response from the master in milliseconds
    this.timeout = options.timeout || 100;

    // how should timeouts be handled - default is resolve(undefined), otherwise reject(Error)
    this.failsafe = options.failsafe === 'reject' ? 'reject' : 'resolve';

    this.parse = options.parse || JSON.parse;
    this.stringify = options.stringify || JSON.stringify;

    // if this is the master thread, we just promisify an lru-cache
    // if it is the worker we need to send messages to the master to resolve the values
    this.promisify = (cluster.isMaster ? master : worker).getPromisified(
      this,
      options
    );
  }

  static async getInstance(options) {
    const lru = new LRUCacheForClustersAsPromised({ ...options, noInit: true });
    if (cluster.isWorker) {
      await lru.promisify('()', options);
    }
    return lru;
  }

  static getAllCaches() {
    if (cluster.isWorker) {
      throw new Error(
        'LRUCacheForClustersAsPromised.getAllCaches() should only be called from the master thread.'
      );
    }
    return master.caches;
  }

  getCache() {
    const caches = LRUCacheForClustersAsPromised.getAllCaches();
    return caches[this.namespace];
  }

  async execute(command, ...args) {
    return this.promisify(command, ...args);
  }

  async set(key, value, maxAge) {
    return this.promisify('set', key, value, maxAge);
  }

  async get(key) {
    return this.promisify('get', key);
  }

  async setObject(key, value, maxAge) {
    return this.promisify('set', key, this.stringify(value), maxAge);
  }

  async getObject(key) {
    const value = await this.promisify('get', key);
    // eslint-disable-next-line no-undefined
    return value ? this.parse(value) : undefined;
  }

  async del(key) {
    return this.promisify('del', key);
  }

  async mGet(keys) {
    return this.promisify('mGet', keys);
  }

  async mSet(pairs, maxAge) {
    return this.promisify('mSet', pairs, maxAge);
  }

  async mGetObjects(keys) {
    const pairs = await this.promisify('mGet', keys);
    const objs = {};
    await utils.mapObjects(pairs, objs, this.parse);
    return objs;
  }

  async mSetObjects(pairs, maxAge) {
    const objs = {};
    await utils.mapObjects(pairs, objs, this.stringify);
    return this.promisify('mSet', objs, maxAge);
  }

  async mDel(keys) {
    return this.promisify('mDel', keys);
  }

  async peek(key) {
    return this.promisify('peek', key);
  }

  async has(key) {
    return this.promisify('has', key);
  }

  async incr(key, amount) {
    return this.promisify('incr', key, amount);
  }

  async decr(key, amount) {
    return this.promisify('decr', key, amount);
  }

  async reset() {
    return this.promisify('reset');
  }

  async keys() {
    return this.promisify('keys');
  }

  async values() {
    return this.promisify('values');
  }

  async dump() {
    return this.promisify('dump');
  }

  async prune() {
    return this.promisify('prune');
  }

  async length() {
    return this.promisify('length');
  }

  async itemCount() {
    return this.promisify('itemCount');
  }

  /**
   * @deprecated use allowStale(stale)
   */
  async stale(stale) {
    return this.allowStale(stale);
  }

  async allowStale(stale) {
    return this.promisify('allowStale', stale);
  }

  async max(max) {
    return this.promisify('max', max);
  }

  async maxAge(maxAge) {
    return this.promisify('maxAge', maxAge);
  }
}

module.exports = LRUCacheForClustersAsPromised;
module.exports.init = () => true;