js/cache/cache_adapter_cache_manager.js
// NPM IMPORTS
import assert from 'assert'
import CacheManager from 'cache-manager' // https://www.npmjs.com/package/cache-manager
// COMMON IMPORTS
import T from '../utils/types'
import CacheAdapter from './cache_adapter'
/**
* Contextual constant for this file logs.
* @private
*/
const context = 'common/cache/cache_adapter_cache_manager'
/*
TODO:
node-cache-manager-redis
node-cache-manager-mongodb
node-cache-manager-mongoose
node-cache-manager-fs
node-cache-manager-fs-binary
node-cache-manager-hazelcast
node-cache-manager-memcached-store
*/
/**
* Cache base class.
*
* @author Luc BORIES
* @license Apache-2.0
*
* @example
* API:
* ->get(arg_key:string, arg_default):Promise
* ->mget(arg_keys:array, arg_default:any|array):Promise
* ->has(arg_key:string):Promise
* ->set(arg_key:string, arg_value, arg_ttl=undefined):Promise
* ->set_ttl(arg_key:string, arg_ttl):Promise
* ->get_ttl(arg_key:string):Promise
* ->get_keys():Promise
* ->remove(arg_keys:string|array):Promise
* ->flush():nothing - delete all entries.
* ->close():nothing - clear interval timeout for checks.
*
*/
export default class CacheAdapterCacheManager extends CacheAdapter
{
/**
* Create Cache instance to manage cached datas.
*
* @param {object} arg_settings - cache engine settings.
*
* @returns {nothing}
*/
constructor(arg_settings)
{
super(arg_settings)
/**
* Class type flag.
* @type {boolean}
*/
this.is_cache_adapter_cache_manager = true
/**
* Cache storage engines.
* @type {array}
*/
this._engine_stores = []
/**
* Cache storage engine.
* @type {CacheManager.engine}
*/
this._engine = undefined
this.init_stores(arg_settings.stores)
if ( this._engine_stores.length > 1 )
{
this._engine = CacheManager.multiCaching(this._engine_stores)
} else if (this._engine_stores.length == 0)
{
const options = {ttl: 5} // seconds
options.ttl = T.isNumber(arg_settings.ttl) ? arg_settings.ttl / 1000 : options.ttl
this._engine = CacheManager.caching({store: 'memory', max: 100, ttl: options.ttl / 1000 /*seconds*/})
} else if (this._engine_stores.length == 1)
{
this._engine = this._engine_stores[0]
}
}
/**
* Create and configure cache stores.
*
* @param {object} arg_stores_settings - stores settings.
*
* @returns {nothing}
*/
init_stores(arg_stores_settings)
{
assert( T.isArray(arg_stores_settings), context + ':init_stores:bad stores settings array' )
arg_stores_settings.forEach( (store_settings, index) => {
assert( T.isObject(store_settings), context + ':init_stores:bad store settings object at index [' + index + ']' )
const store_type = store_settings.type
assert( T.isString(store_type), context + ':init_stores:bad store type string at index [' + index + ']' )
let store = undefined
switch(store_type) {
case 'memory':
{
const cfg_ttl = store_settings.ttl ? store_settings.ttl / 1000 : 10
const cfg_max = store_settings.max ? store_settings.max : 100
store = CacheManager.caching({store: 'memory', max: cfg_max, ttl: cfg_ttl/*seconds*/})
break
}
case 'memcached':
{
try {
const memcachedStore = require('cache-manager-memcached-store')
const cfg_host = store_settings.host ? store_settings.host : 'localhost'
const cfg_port = store_settings.port ? store_settings.port : '11211'
const cfg_options = store_settings.options ? store_settings.options : {}
store = CacheManager.caching({
store: memcachedStore,
host: cfg_host,
port: cfg_port,
memcached: cfg_options
})
} catch(e)
{
console.error(context + ':init_stores:memcached error')
}
break
}
case 'redis':
{
try {
const redisStore = require('cache-manager-redis')
const cfg_ttl = store_settings.ttl ? store_settings.ttl/1000 : 1
const cfg_host = store_settings.host ? store_settings.host : 'localhost'
const cfg_port = store_settings.port ? store_settings.port : '6379'
const cfg_auth_path = store_settings.auth_path ? store_settings.auth_path : undefined
const cfg_db = store_settings.db ? store_settings.db : undefined
store = CacheManager.caching({
store: redisStore ,
host: cfg_host,
port: cfg_port,
auth_path: cfg_auth_path,
db: cfg_db,
ttl:cfg_ttl
})
} catch(e)
{
console.error(context + ':init_stores:memcached error')
}
break
}
}
if (store)
{
this._engine_stores.push(store)
}
})
}
/**
* Get a cached value.
*
* @param {string} arg_key - key string to retrieve the value.
* @param {any} arg_default - returned default value if the key is not found (optional) (default:undefined).
*
* @returns {Promise} - Promise of cached value or undefined.
*/
get(arg_key, arg_default=undefined)
{
let promise = new Promise(
(resolve, reject)=>{
this._engine.get(arg_key, (err, value)=> {
if (err)
{
reject(err)
return
}
if (value == undefined)
{
resolve(arg_default)
return
}
resolve(value)
})
}
)
return promise
}
/**
* Get a cached value.
*
* @param {array} arg_keys - key strings to retrieve the values.
* @param {any} arg_default - returned default value if the key is not found (optional) (default:undefined).
*
* @returns {Promise} - Promise of cached value or undefined array.
*/
mget(arg_keys, arg_default=undefined)
{
if ( T.isString(arg_keys) )
{
return this.get(arg_keys, arg_default)
}
if ( ! T.isArray(arg_keys) )
{
return Promise.reject('bad keys array')
}
let promises = []
this.arg_keys.forEach(
(arg_key)=>{
const promise = new Promise(
(resolve, reject)=>{
this._engine.get(arg_key, (err, value)=> {
if (err)
{
reject(err)
return
}
if (value == undefined)
{
resolve(arg_default)
return
}
resolve(value)
})
}
)
promises.push(promise)
}
)
return promises
}
/**
* Test if given key has a cached value.
*
* @param {string} arg_key - key string to retrieve the value.
*
* @returns {Promise} - Promise of cached value or undefined.
*/
has(arg_key)
{
// let promise = new Promise(
// (resolve, reject)=>{
// this._engine.get(arg_key, (err, value)=> {
// if (err)
// {
// reject(err)
// return
// }
// resolve(value == undefined ? false : true)
// })
// }
// )
// return promise
return this._engine.get(arg_key).then( (result)=>{ return (result == undefined ? false : true) } )
}
/**
* Set a cached value with given key.
*
* @param {string} arg_key - key string to retrieve the value.
* @param {any} arg_value - value to cache.
* @param {integer} arg_ttl - time to leave for cached value.
*
* @returns {Promise} - Promise of nothing.
*/
set(arg_key, arg_value, arg_ttl=undefined)
{
arg_ttl = T.isNumber(arg_ttl) ? arg_ttl / 1000 : undefined
let promise = new Promise(
(resolve, reject)=>{
this._engine.set(arg_key, arg_value, { ttl:arg_ttl }, (err, success)=> {
if (err)
{
reject(err)
return
}
resolve(success ? true : false)
})
}
)
return promise
}
/**
* Set cached value ttl.
*
* @param {string} arg_key - key string to retrieve the value.
* @param {integer} arg_ttl - time to leave for cached value.
*
* @returns {Promise} - Promise of nothing.
*/
set_ttl(arg_key, arg_ttl)
{
if ( T.isNumber(arg_ttl) )
{
return this.get(arg_key).then( (value)=>{ this.set(arg_key, value, arg_ttl / 1000) } )
}
}
/**
* Get cached value ttl.
*
* @param {string} arg_key - key string to retrieve the value.
*
* @returns {Promise} - Promise of integer|undefined.
*/
get_ttl(arg_key)
{
let promise = new Promise(
(resolve, reject)=>{
this._engine.ttl(arg_key, (err, value)=> {
if (err)
{
reject(err)
return
}
resolve(IDBCursorWithValue)
})
}
)
return promise
}
/**
* Get cached keys.
*
* @returns {Promise} - Promise of keys array.
*/
get_keys()
{
let promise = new Promise(
(resolve, reject)=>{
this._engine.keys( (err, keys)=> {
if (err)
{
reject(err)
return
}
resolve(keys)
})
}
)
return promise
}
/**
* Remove a cached value.
*
* @param {string|array} arg_keys - value key string or array.
*
* @returns {Promise} - Promise of undefined.
*/
remove(arg_keys)
{
if ( T.isString(arg_keys) )
{
let promise = new Promise(
(resolve, reject)=>{
this._engine.del(arg_keys, (err, keys)=> {
if (err)
{
reject(err)
return
}
resolve(keys)
})
}
)
return promise
}
if ( ! T.isArray(arg_keys) )
{
return Promise.reject('bad keys string or array')
}
let promises = []
this.arg_keys.forEach(
(arg_key)=>{
const promise = this.remove(arg_key)
promises.push(promise)
}
)
return promises
}
/**
* Flush all cached values (clear interval timeout for checks).
*
* @returns {Promise} - Promise of undefined.
*/
flush()
{
let promise = new Promise(
(resolve, reject)=>{
this._engine.reset( (err, undefined)=> {
if (err)
{
reject(err)
return
}
resolve()
})
}
)
return promise
}
/**
* Close cache engine.
*
* @returns {Promise} - Promise of undefined.
*/
close()
{
return this.flus()
}
}