"use strict"; const redisIO = require("ioredis"), //https://github.com/luin/ioredis Promise = require("bluebird"); import { defaultsDeep } from "lodash"; import { loadConfig } from "my-nconf"; import { logger as defaultLogger } from "../utils/Logger"; import { AppError } from "../errors"; redisIO.Promise = Promise; export class Redis { private _nodesConfig: any = null; private _clusterOptions: any = null; private _cluster: any = null; private logger: any = null; private static _instance; static get Instance() { if (!this._instance) { let c = loadConfig(); this._instance = new Redis(c.get("redis_cluster")); } return this._instance; } /** * [getObject get object of key in this redis store] * @param {[type]} redis [the redis store to retrieve object from] * @param {[type]} withKey [unique key] * @param {[type]} defaultReturnVal = undefined [value to return if defined & key doesnt exist] * @param {[type]} callback = undefined [description] */ static getObject( redis_inst, withKey, defaultReturnVal = undefined, callback = undefined ) { return new Promise((resolve, reject) => { redis_inst.get(withKey, (err, result) => { if (err) return reject(err); //redis err try { resolve(JSON.parse(result)); } catch (err) { return defaultReturnVal !== undefined ? resolve(defaultReturnVal) : reject(new AppError(err)); //jsonparse error } }); }).nodeify(callback); } constructor(_nodesConfig?, _clusterOptions?, _logger = defaultLogger) { let my = this; my.logger = _logger; redisIO.Promise.onPossiblyUnhandledRejection(function(error) { // you can log the error here. // error.command.name is the command name, here is 'set' // error.command.args is the command arguments, here is ['foo'] error.command ? my.logger.error(error, { name: error.command.name, args: error.command.args }) : my.logger.error(error); }); my._nodesConfig = _nodesConfig || [ { port: 6380, host: "127.0.0.1" }, { port: 6381, host: "127.0.0.1" }, { port: 6382, host: "127.0.0.1" } ]; my._clusterOptions = defaultsDeep({}, _clusterOptions, { enableReadyCheck: true, scaleReads: "all", //Send write queries to masters and read queries to masters or slaves randomly maxRedirections: 16, retryDelayOnFailover: 100, retryDelayOnClusterDown: 100, retryDelayOnTryAgain: 100, redisOptions: { //passssword: 'fallback-password' showFriendlyErrorStack: /** only in these envs */ [ "development", "test" ].indexOf(process.env.NODE_ENV || "undefined") } }); my._cluster = (_nodesConfig.length > 1 ? new redisIO.Cluster(_nodesConfig, _clusterOptions) : new redisIO(defaultsDeep({}, _nodesConfig[0], _clusterOptions)) ) .on("connect", () => { my.logger.debug("redis connecting to", _nodesConfig); }) .on("ready", () => { if (_nodesConfig.length > 1) { let slaves = my._cluster.nodes("slave"); let masters = my._cluster.nodes("master"); my.logger.info( "redis cluster online with %d masters & %d slaves", slaves.length, masters.length ); my._cluster .on("-node", function(node) { //node disconnected my.logger.debug(node.options.key, node.status); }) .on("+node", function(node) { //node added my.logger.debug(node.options.key, node.status); }); } else { my.logger.debug("redis instance online @", _nodesConfig[0]); } }) .on("error", err => { my.logger.error(new AppError(err)); my.logger.error("node error", err.lastNodeError); }); return my._cluster; } }