'use strict';
/**
* Module dependencies, required for ALL Twyr' modules
* @ignore
*/
/**
* Module dependencies, required for this module
* @ignore
*/
const TwyrBaseService = require('twyr-base-service').TwyrBaseService;
const TwyrSrvcError = require('twyr-service-error').TwyrServiceError;
/**
* @class CacheService
* @extends {TwyrBaseService}
* @classdesc The Twyr Web Application Server Cache Service.
*
* @description
* Allows the rest of the Twyr Modules to store / retrieve data in the cache.
*
*/
class CacheService extends TwyrBaseService {
// #region Constructor
constructor(parent, loader) {
super(parent, loader);
}
// #endregion
// #region startup/teardown code
/**
* @async
* @function
* @override
* @instance
* @memberof CacheService
* @name _setup
*
* @returns {Promise} Promise that resolves / rejects based on whether the connection request went through.
*
* @summary Sets up the connection to the configured Redis Server.
*/
async _setup() {
const Promise = require('bluebird');
await super._setup();
return new Promise((resolve, reject) => {
try {
this.$config.options['retry_strategy'] = (options) => {
if(options.error.code === 'ECONNREFUSED')
return new TwyrSrvcError(`${this.name}::retry_strategy::Server refused connection`, options.error);
if(options.total_retry_time > 1000 * 60 * 60)
return new TwyrSrvcError('Retry time exhausted');
if(options.times_connected > 10)
return undefined;
// reconnect after
return Math.max(options.attempt * 100, 3000);
};
const promises = require('bluebird');
const redis = require('redis');
redis.RedisClient.prototype = promises.promisifyAll(redis.RedisClient.prototype);
redis.Multi.prototype = promises.promisifyAll(redis.Multi.prototype);
this.$cache = redis.createClient(this.$config.port, this.$config.host, this.$config.options);
this.$cache.once('connect', async (status) => {
this.$cache.on('error', this._handleRedisError.bind(this));
if(twyrEnv === 'development') {
this.$monitorClient = this.$cache.duplicate();
this.$monitorClient.on('monitor', this._logRedisCommands.bind(this));
await this.$monitorClient.monitorAsync();
}
if(resolve) resolve(status);
});
this.$cache.once('error', (err) => {
if(reject) reject(new TwyrSrvcError(`${this.name}::_setup::Cache error`, err));
});
}
catch(err) {
if(reject) reject(new TwyrSrvcError(`${this.name}::_setup::Connection error`, err));
}
});
}
/**
* @async
* @function
* @override
* @instance
* @memberof CacheService
* @name _teardown
*
* @returns {undefined} Nothing.
*
* @summary Quits the connection to the configured Redis Server.
*/
async _teardown() {
try {
if(twyrEnv === 'development' && this.$monitorClient) {
await this.$monitorClient.quitAsync();
this.$monitorClient.end(true);
delete this.$monitorClient;
}
if(!this.$cache) return null;
await this.$cache.quitAsync();
this.$cache.end(true);
delete this.$cache;
await super._teardown();
return null;
}
catch(err) {
throw new TwyrSrvcError(`${this.name}::_teardown error`, err);
}
}
// #endregion
// #region Private Methods
_logRedisCommands(time, redisArgs) {
redisArgs['level'] = this.$config.monitorLogLevel || 'silly';
redisArgs['message'] = 'Redis Command Monitoring';
this.$dependencies.LoggerService.log(redisArgs);
}
_handleRedisError(err) {
this.$dependencies.LoggerService.error(new TwyrSrvcError(`${this.name}::_handleRedisError error`, err).toString());
}
// #endregion
// #region Properties
/**
* @override
*/
get Interface() {
return this.$cache;
}
/**
* @override
*/
get dependencies() {
return ['ConfigurationService', 'LoggerService'].concat(super.dependencies);
}
/**
* @override
*/
get basePath() {
return __dirname;
}
// #endregion
}
exports.service = CacheService;