diff --git a/.eslintrc b/.eslintrc
index 2e2b313..9a6c0e9 100644
--- a/.eslintrc
+++ b/.eslintrc
@@ -5,12 +5,8 @@
"node": true,
"jest": true
},
- "parserOptions": {
- "ecmaVersion": 2018
- },
"rules": {
- "no-multi-assign": 1,
- "func-names": 1,
- "no-underscore-dangle": 1
+ "arrow-parens": ["error", "as-needed"],
+ "no-underscore-dangle": 0
}
}
diff --git a/.prettierrc b/.prettierrc
index c50384f..a998616 100644
--- a/.prettierrc
+++ b/.prettierrc
@@ -2,5 +2,6 @@
"trailingComma": "none",
"tabWidth": 2,
"semi": false,
- "singleQuote": true
+ "singleQuote": true,
+ "arrowParens": "avoid"
}
diff --git a/docs/redisCache.js.html b/docs/redisCache.js.html
index 2e3d8a6..a081a0b 100644
--- a/docs/redisCache.js.html
+++ b/docs/redisCache.js.html
@@ -113,7 +113,7 @@
redisCache.js
let options = {}
let store = null
-const getNumber = num => !Number.isNaN(num) && num >= 0 ? num : null
+const getNumber = (num) => (!Number.isNaN(num) && num >= 0 ? num : null)
/**
* @param {object} options
@@ -182,7 +182,7 @@ redisCache.js
* Sets the ttlInSeconds
* @returns {number} ttl
*/
-exports.setTtlInSeconds = ttl => {
+exports.setTtlInSeconds = (ttl) => {
options.ttlInSeconds = getNumber(ttl)
return options.ttlInSeconds
}
@@ -216,7 +216,7 @@ redisCache.js
* @param {string} key - key for the value stored
* @returns {any} value or null when the key is missing
*/
-exports.get = key => this.getStore().get(key)
+exports.get = (key) => this.getStore().get(key)
/**
* Returns all keys matching pattern
@@ -247,9 +247,7 @@ redisCache.js
* @property {number} opts.ttlInSeconds - time to live in seconds
* @returns {string} 'OK' if successful
*/
-exports.wrap = async (key, fn, {
- ttlInSeconds
-} = {}) => {
+exports.wrap = async (key, fn, { ttlInSeconds } = {}) => {
const ttl = getNumber(ttlInSeconds) || options.ttlInSeconds
if (ttl && ttl === 0) {
diff --git a/lib/redisCache.js b/lib/redisCache.js
index 62b7bd6..07273f3 100644
--- a/lib/redisCache.js
+++ b/lib/redisCache.js
@@ -8,7 +8,7 @@ const NotInitialisedError = require('./NotInitialisedError')
let options = {}
let store = null
-const getNumber = (num) => (!Number.isNaN(num) && num >= 0 ? num : null)
+const getNumber = num => (!Number.isNaN(num) && num >= 0 ? num : 0)
/**
* @param {object} options
@@ -32,7 +32,7 @@ exports.init = ({
redisOptions,
poolOptions,
logger: createLogger(logger),
- ttlInSeconds
+ ttlInSeconds: getNumber(ttlInSeconds)
}
store = new RedisStore(options)
@@ -77,7 +77,7 @@ exports.getTtlInSeconds = () => options.ttlInSeconds
* Sets the ttlInSeconds
* @returns {number} ttl
*/
-exports.setTtlInSeconds = (ttl) => {
+exports.setTtlInSeconds = ttl => {
options.ttlInSeconds = getNumber(ttl)
return options.ttlInSeconds
}
@@ -111,7 +111,7 @@ exports.getset = async (key, value, ttlInSeconds) => {
* @param {string} key - key for the value stored
* @returns {any} value or null when the key is missing
*/
-exports.get = (key) => this.getStore().get(key)
+exports.get = key => this.getStore().get(key)
/**
* Returns all keys matching pattern
@@ -145,7 +145,7 @@ exports.deleteAll = (pattern = '*') => this.getStore().deleteAll(pattern)
exports.wrap = async (key, fn, { ttlInSeconds } = {}) => {
const ttl = getNumber(ttlInSeconds) || options.ttlInSeconds
- if (ttl && ttl === 0) {
+ if (ttl === 0) {
debug(`Not caching, invalid ttl: ${ttlInSeconds}`)
return fn()
}
diff --git a/lib/redisCache.test.js b/lib/redisCache.test.js
index 79c90b3..b583e43 100644
--- a/lib/redisCache.test.js
+++ b/lib/redisCache.test.js
@@ -8,6 +8,7 @@ const {
getPoolOptions,
getRedisOptions,
getset,
+ getStore,
getTtlInSeconds,
keys,
set,
@@ -22,17 +23,9 @@ describe('redisCache', () => {
host: process.env.REDIS_HOST || '127.0.0.1',
auth_pass: process.env.REDIS_AUTH
}
- const poolOptions = {
- min: 2,
- max: 4
- }
+ const poolOptions = { min: 2, max: 4 }
const ttlInSeconds = 10
- const options = {
- name,
- redisOptions,
- poolOptions,
- ttlInSeconds
- }
+ const options = { name, redisOptions, poolOptions, ttlInSeconds }
init(options)
const key = 'chuck-norris'
@@ -69,6 +62,12 @@ describe('redisCache', () => {
})
})
+ describe('getStore', () => {
+ test('returns store', () => {
+ expect(getStore()).not.toBeNull()
+ })
+ })
+
describe('status', () => {
test('get store stats', () => {
const { name: statusName, size, available, pending } = status()
@@ -126,6 +125,10 @@ describe('redisCache', () => {
beforeAll(() => deleteAll())
+ afterEach(() => {
+ setTtlInSeconds(ttlInSeconds)
+ })
+
test("set if key doesn't exist", async () => {
const localKey = genRandomStr()
@@ -146,6 +149,7 @@ describe('redisCache', () => {
test('do nothing when ttlInSeconds=0', async () => {
const localKey = genRandomStr()
+ setTtlInSeconds(0)
const result = await wrap(localKey, fn, {
ttlInSeconds: 0
@@ -154,8 +158,9 @@ describe('redisCache', () => {
expect(result).toBe(newValue)
})
- test('do nothing when ttlInSeconds=0', async () => {
+ test('do nothing when ttlInSeconds < 0', async () => {
const localKey = genRandomStr()
+ setTtlInSeconds(0)
const result = await wrap(localKey, fn, {
ttlInSeconds: -1
@@ -166,6 +171,7 @@ describe('redisCache', () => {
test('do nothing when ttlInSeconds is invalid', async () => {
const localKey = genRandomStr()
+ setTtlInSeconds('NOT_NUMBER')
const result = await wrap(localKey, fn, {
ttlInSeconds: 'NOT_NUMBER'
@@ -176,14 +182,11 @@ describe('redisCache', () => {
})
describe('keys', () => {
- const keyValues = {
- key1: 'value1',
- 'test:key2': 'value2'
- }
+ const keyValues = { key1: 'value1', 'test:key2': 'value2' }
beforeAll(() => deleteAll())
beforeEach(() =>
- Promise.all(Object.keys(keyValues).map((k) => set(k, keyValues[k])))
+ Promise.all(Object.keys(keyValues).map(k => set(k, keyValues[k])))
)
test('return all the keys', async () =>
@@ -203,14 +206,11 @@ describe('redisCache', () => {
})
describe('del', () => {
- const keyValues = {
- key1: 'value1',
- key2: 'value2'
- }
+ const keyValues = { key1: 'value1', key2: 'value2' }
beforeEach(async () => {
await deleteAll()
- await Promise.all(Object.keys(keyValues).map((k) => set(k, keyValues[k])))
+ await Promise.all(Object.keys(keyValues).map(k => set(k, keyValues[k])))
})
test('delete keys array', async () => {
@@ -230,13 +230,10 @@ describe('redisCache', () => {
})
describe('deleteAll', () => {
- const keyValues = {
- key1: 'value1',
- key2: 'value2'
- }
+ const keyValues = { key1: 'value1', key2: 'value2' }
beforeEach(() =>
- Promise.all(Object.keys(keyValues).map((k) => set(k, keyValues[k])))
+ Promise.all(Object.keys(keyValues).map(k => set(k, keyValues[k])))
)
test('delete all the keys', async () => {
diff --git a/lib/redisConnectionPool.js b/lib/redisConnectionPool.js
index c371962..e06b93a 100644
--- a/lib/redisConnectionPool.js
+++ b/lib/redisConnectionPool.js
@@ -1,4 +1,4 @@
-const debug = require('debug')('nodeRedisConnectionPool')
+const debug = require('debug')('nodeRedisPool')
const util = require('util')
const genericPool = require('generic-pool')
const retry = require('retry-as-promised')
@@ -7,12 +7,12 @@ const redis = require('redis')
const createLogger = require('./createLogger')
const genRandomStr = require('./genRandomStr')
-const create = function (redisOptions) {
+const createClient = redisOptions => {
return new Promise((resolve, reject) => {
debug('Start redis createClient', redisOptions)
const client = redis.createClient(redisOptions)
- client.on('error', (err) => {
+ client.on('error', err => {
debug('Failed redis createClient', err)
reject(err)
})
@@ -23,9 +23,9 @@ const create = function (redisOptions) {
})
}
-const selectDB = function (client, db) {
+const selectDB = (client, db) => {
return new Promise((resolve, reject) => {
- client.select(db, (err) => {
+ client.select(db, err => {
if (err) reject(err)
debug('DB selected: ', db)
resolve(client)
@@ -41,241 +41,237 @@ const selectDB = function (client, db) {
* @param {object} options.poolOptions - opts from [node-pool#createpool]{@link https://github.com/coopernurse/node-pool#createpool}
* @param {object} options.logger - Inject your custom logger
*/
-const RedisPool = (module.exports = function ({
- name,
- redisOptions,
- poolOptions,
- logger
-} = {}) {
- this.name = name || `redisPool-${genRandomStr()}`
- this.redisOptions = redisOptions
- this.poolOptions = poolOptions || {}
- this.logger = createLogger(logger)
+module.exports = class RedisPool {
+ constructor({ name, redisOptions, poolOptions, logger } = {}) {
+ this.name = name || `redisPool-${genRandomStr()}`
+ this.redisOptions = redisOptions
+ this.poolOptions = poolOptions || {}
+ this.logger = createLogger(logger)
- const factory = {
- create: () => {
- // for retry
- let createAttempts = 0
+ const factory = {
+ create: () => {
+ // for retry
+ let createAttempts = 0
- // this is due to the limitation of node-pool ATM
- // https://github.com/coopernurse/node-pool/issues/161#issuecomment-261109882
- return retry(
- () => {
- createAttempts += 1
- if (createAttempts > 3) {
- const err = new Error(
- `Failed redis createClient, ${JSON.stringify(redisOptions || {})}`
- )
- err.name = 'CONN_FAILED'
+ // this is due to the limitation of node-pool ATM
+ // https://github.com/coopernurse/node-pool/issues/161#issuecomment-261109882
+ return retry(
+ () => {
+ createAttempts += 1
+ if (createAttempts > 3) {
+ const err = new Error(
+ `Failed redis createClient, ${JSON.stringify(
+ redisOptions || {}
+ )}`
+ )
+ err.name = 'CONN_FAILED'
+ debug(
+ 'Max conn createAttempts reached: %s, resolving to error:',
+ createAttempts,
+ err
+ )
+
+ // reset for next try
+ createAttempts = 0
+ return Promise.resolve(err)
+ }
+
+ return createClient(redisOptions)
+ },
+ {
+ max: 10,
+ name: 'factory.create',
+ report: debug
+ }
+ )
+ },
+ destroy: client =>
+ new Promise(resolve => {
+ try {
+ // Flush when closing.
+ client.end(true, () => resolve())
debug(
- 'Max conn createAttempts reached: %s, resolving to error:',
- createAttempts,
- err
+ 'Client conn closed. Available count : %s. Pool size: %s',
+ this.availableCount(),
+ this.getPoolSize()
)
+ this.logger.log(
+ 'Client conn closed. Available count : %s. Pool size: %s',
+ this.availableCount(),
+ this.getPoolSize()
+ )
+ } catch (err) {
+ debug('Failed to destroy connection', err)
+ this.logger.error('Failed to destroy connection', err)
- // reset for next try
- createAttempts = 0
- return Promise.resolve(err)
+ // throw error cause infinite event loop; limitation of node-pool
+ // throw err;
}
+ })
+ }
- return create(redisOptions)
- },
- {
- max: 10,
- name: 'factory.create',
- report: debug
- }
- )
- },
- destroy: (client) =>
- new Promise((resolve) => {
- try {
- // Flush when closing.
- client.end(true, () => resolve())
- debug(
- 'Client conn closed. Available count : %s. Pool size: %s',
- this.availableCount(),
- this.getPoolSize()
- )
- this.logger.log(
- 'Client conn closed. Available count : %s. Pool size: %s',
- this.availableCount(),
- this.getPoolSize()
- )
- } catch (err) {
- debug('Failed to destroy connection', err)
- this.logger.error('Failed to destroy connection', err)
+ // Now that the pool settings are ready create a pool instance.
+ debug('Creating pool', this.poolOptions)
+ this.pool = genericPool.createPool(factory, this.poolOptions)
- // throw error cause infinite event loop; limitation of node-pool
- // throw err;
- }
- })
+ this.pool.on('factoryCreateError', e => {
+ debug('Errored while connecting Redis', e)
+ this.logger.error('Errored while connecting Redis', e)
+ })
+ this.pool.on('factoryDestroyError', e => {
+ debug('Errored while destroying Redis conn', e)
+ this.logger.error('Errored while destroying Redis conn', e)
+ })
}
- // Now that the pool settings are ready create a pool instance.
- debug('Creating pool', this.poolOptions)
- this.pool = genericPool.createPool(factory, this.poolOptions)
+ /**
+ * Send redis instructions
+ *
+ * @param {string} commandName - Name of the command
+ * @param {array} commandArgs - Args sent to the command
+ * @returns {promise} Promise resolve with the result or Error
+ */
+ async sendCommand(commandName, commandArgs = []) {
+ const args = [].concat(commandArgs)
+ debug('Executing send_command', commandName, args)
- this.pool.on('factoryCreateError', (e) => {
- debug('Errored while connecting Redis', e)
- this.logger.error('Errored while connecting Redis', e)
- })
- this.pool.on('factoryDestroyError', (e) => {
- debug('Errored while destroying Redis conn', e)
- this.logger.error('Errored while destroying Redis conn', e)
- })
-})
+ const conn = await this.pool.acquire(
+ this.poolOptions.priorityRange || 1,
+ this.redisOptions.db
+ )
-/**
- * Send redis instructions
- *
- * @param {string} commandName - Name of the command
- * @param {array} commandArgs - Args sent to the command
- * @returns {promise} Promise resolve with the result or Error
- */
-RedisPool.prototype.sendCommand = async function (
- commandName,
- commandArgs = []
-) {
- const args = [].concat(commandArgs)
- debug('Executing send_command', commandName, args)
+ const sendCommand = util.promisify(conn.send_command).bind(conn)
+ let result
+ try {
+ result = await sendCommand(commandName, args.length > 0 ? args : null)
+ this.pool.release(conn)
+ } catch (error) {
+ this.pool.release(conn)
+ this.logger.error('Errored send_command', error)
+ debug('Errored send_command', error)
+ throw error
+ }
- const conn = await this.pool.acquire(
- this.poolOptions.priorityRange || 1,
- this.redisOptions.db
- )
-
- const sendCommand = util.promisify(conn.send_command).bind(conn)
- let result
- try {
- result = await sendCommand(commandName, args.length > 0 ? args : null)
- this.pool.release(conn)
- } catch (error) {
- this.pool.release(conn)
- this.logger.error('Errored send_command', error)
- debug('Errored send_command', error)
- throw error
+ return result
}
- return result
-}
+ /**
+ * Acquire a Redis connection and use an optional priority.
+ *
+ * @param {number} priority - priority list number
+ * @param {number} db - Use the db with range {0-16}
+ * @returns {promise} Promise resolve with the connection or Error
+ */
+ async acquire(priority, db) {
+ const client = await this.pool.acquire(priority)
+ if (client instanceof Error) {
+ debug("Couldn't acquire connection to %j", this.redisOptions)
+ this.logger.error("Couldn't acquire connection to %j", this.redisOptions)
+ throw client
+ }
-/**
- * Acquire a Redis connection and use an optional priority.
- *
- * @param {number} priority - priority list number
- * @param {number} db - Use the db with range {0-16}
- * @returns {promise} Promise resolve with the connection or Error
- */
-RedisPool.prototype.acquire = async function (priority, db) {
- const client = await this.pool.acquire(priority)
- if (client instanceof Error) {
- debug("Couldn't acquire connection to %j", this.redisOptions)
- this.logger.error("Couldn't acquire connection to %j", this.redisOptions)
- throw client
+ if (db) {
+ this.logger.info('select DB:', db)
+ return selectDB(client, db)
+ }
+ return client
}
- if (db) {
- this.logger.info('select DB:', db)
- return selectDB(client, db)
+ /**
+ * Release a Redis connection to the pool.
+ *
+ * @param {object} client - Redis connection
+ * @returns {promise} Promise
+ */
+ release(client) {
+ return this.pool.release(client)
}
- return client
-}
-/**
- * Release a Redis connection to the pool.
- *
- * @param {object} client - Redis connection
- * @returns {promise} Promise
- */
-RedisPool.prototype.release = function (client) {
- return this.pool.release(client)
-}
-
-/**
- * Destroy a Redis connection.
- *
- * @param {object} client - Redis connection
- * @returns {promise} Promise
- */
-RedisPool.prototype.destroy = function (client) {
- return this.pool.destroy(client)
-}
+ /**
+ * Destroy a Redis connection.
+ *
+ * @param {object} client - Redis connection
+ * @returns {promise} Promise
+ */
+ destroy(client) {
+ return this.pool.destroy(client)
+ }
-/**
- * Drains the connection pool and call the callback id provided.
- *
- * @returns {promise} Promise
- */
-RedisPool.prototype.drain = async function () {
- await this.pool.drain()
- this.pool.clear()
-}
+ /**
+ * Drains the connection pool and call the callback id provided.
+ *
+ * @returns {promise} Promise
+ */
+ async drain() {
+ await this.pool.drain()
+ this.pool.clear()
+ }
-/**
- * Returns factory.name for this pool
- *
- * @returns {string} Name of the pool
- */
-RedisPool.prototype.getName = function () {
- return this.name
-}
+ /**
+ * Returns factory.name for this pool
+ *
+ * @returns {string} Name of the pool
+ */
+ getName() {
+ return this.name
+ }
-/**
- * Returns this.redisOptions for this pool
- *
- * @returns {object} redis options given
- */
-RedisPool.prototype.getRedisOptions = function () {
- return this.redisOptions
-}
+ /**
+ * Returns this.redisOptions for this pool
+ *
+ * @returns {object} redis options given
+ */
+ getRedisOptions() {
+ return this.redisOptions
+ }
-/**
- * Returns this.poolOptions for this pool
- *
- * @returns {object} pool options given
- */
-RedisPool.prototype.getPoolOptions = function () {
- return this.poolOptions
-}
+ /**
+ * Returns this.poolOptions for this pool
+ *
+ * @returns {object} pool options given
+ */
+ getPoolOptions() {
+ return this.poolOptions
+ }
-/**
- * Returns size of the pool
- *
- * @returns {number} size of the pool
- */
-RedisPool.prototype.getPoolSize = function () {
- return this.pool.size
-}
+ /**
+ * Returns size of the pool
+ *
+ * @returns {number} size of the pool
+ */
+ getPoolSize() {
+ return this.pool.size
+ }
-/**
- * Returns available connections count of the pool
- *
- * @returns {number} available connections count of the pool
- */
-RedisPool.prototype.availableCount = function () {
- return this.pool.available
-}
+ /**
+ * Returns available connections count of the pool
+ *
+ * @returns {number} available connections count of the pool
+ */
+ availableCount() {
+ return this.pool.available
+ }
-/**
- * Returns pending connections count of the pool
- *
- * @returns {number} pending connections count of the pool
- */
-RedisPool.prototype.pendingCount = function () {
- return this.pool.pending
-}
+ /**
+ * Returns pending connections count of the pool
+ *
+ * @returns {number} pending connections count of the pool
+ */
+ pendingCount() {
+ return this.pool.pending
+ }
-/**
- * Returns pool status and stats
- *
- * @returns {object} pool status and stats
- */
-RedisPool.prototype.status = function () {
- return {
- name: this.name,
- size: this.pool.size,
- available: this.pool.available,
- pending: this.pool.pending
+ /**
+ * Returns pool status and stats
+ *
+ * @returns {object} pool status and stats
+ */
+ status() {
+ return {
+ name: this.name,
+ size: this.pool.size,
+ available: this.pool.available,
+ pending: this.pool.pending
+ }
}
}
diff --git a/lib/redisConnectionPool.test.js b/lib/redisConnectionPool.test.js
index 8f1315c..9053846 100644
--- a/lib/redisConnectionPool.test.js
+++ b/lib/redisConnectionPool.test.js
@@ -6,15 +6,8 @@ describe('redisConnectionPool', () => {
host: process.env.REDIS_HOST || '127.0.0.1',
auth_pass: process.env.REDIS_AUTH
}
- const poolOptions = {
- min: 2,
- max: 4
- }
- const options = {
- name,
- redisOptions,
- poolOptions
- }
+ const poolOptions = { min: 2, max: 4 }
+ const options = { name, redisOptions, poolOptions }
let pool
beforeEach(() => {
@@ -85,10 +78,7 @@ describe('redisConnectionPool', () => {
})
test('wait to acquire if all used up', async () => {
- const localPoolOptions = {
- min: 0,
- max: 1
- }
+ const localPoolOptions = { min: 0, max: 1 }
const localPool = new RedisPool({
...options,
poolOptions: localPoolOptions
@@ -121,10 +111,7 @@ describe('redisConnectionPool', () => {
test('not fail with many higher min connections', async () => {
const localPool = new RedisPool({
...options,
- poolOptions: {
- min: 5,
- max: 10
- }
+ poolOptions: { min: 5, max: 10 }
})
const client = await localPool.acquire()
@@ -134,10 +121,7 @@ describe('redisConnectionPool', () => {
test('invalid host fail acquire connection', async () => {
const localPool = new RedisPool({
...options,
- redisOptions: {
- ...redisOptions,
- host: 'UNAVAILABLE_HOST'
- }
+ redisOptions: { ...redisOptions, host: 'UNAVAILABLE_HOST' }
})
await expect(localPool.acquire()).rejects.toThrowError(
@@ -148,17 +132,14 @@ describe('redisConnectionPool', () => {
test('conn timeout fail acquire connection', async () => {
const localPool = new RedisPool({
...options,
- poolOptions: {
- min: 1,
- acquireTimeoutMillis: 1
- }
+ poolOptions: { min: 1, acquireTimeoutMillis: 1 }
})
// make the conn is in-use
- await new Promise((r) => setTimeout(r, 300))
+ await new Promise(r => setTimeout(r, 300))
await localPool.acquire()
- await new Promise((r) => setTimeout(r, 1500))
+ await new Promise(r => setTimeout(r, 1500))
expect(() => localPool.acquire()).rejects.toThrowError(
'ResourceRequest timed out'
)
@@ -168,7 +149,7 @@ describe('redisConnectionPool', () => {
describe('release', () => {
test('release connection with valid host', async () => {
const localPool = new RedisPool(options)
- await new Promise((r) => setTimeout(r, 300))
+ await new Promise(r => setTimeout(r, 300))
expect(localPool.availableCount()).toBe(poolOptions.min)
expect(localPool.getPoolSize()).toBe(poolOptions.min)
@@ -185,10 +166,7 @@ describe('redisConnectionPool', () => {
test('release connection with invalid host', async () => {
const localPool = new RedisPool({
...options,
- redisOptions: {
- ...redisOptions,
- host: 'UNAVAILABLE_HOST'
- }
+ redisOptions: { ...redisOptions, host: 'UNAVAILABLE_HOST' }
})
expect(localPool.release()).rejects.toThrowError(
@@ -200,7 +178,7 @@ describe('redisConnectionPool', () => {
describe('destroy', () => {
test('destroy connection with valid host', async () => {
const localPool = new RedisPool(options)
- await new Promise((r) => setTimeout(r, 300))
+ await new Promise(r => setTimeout(r, 300))
expect(localPool.availableCount()).toBe(poolOptions.min)
expect(localPool.getPoolSize()).toBe(poolOptions.min)
@@ -211,7 +189,7 @@ describe('redisConnectionPool', () => {
expect(localPool.availableCount()).toBe(poolOptions.min - 1)
await localPool.destroy(client)
- await new Promise((r) => setTimeout(r, 300))
+ await new Promise(r => setTimeout(r, 300))
expect(localPool.availableCount()).toBe(poolOptions.min)
})
})
@@ -219,7 +197,7 @@ describe('redisConnectionPool', () => {
describe('drain', () => {
test('drain all the coonections', async () => {
const localPool = new RedisPool(options)
- await new Promise((r) => setTimeout(r, 300))
+ await new Promise(r => setTimeout(r, 300))
expect(localPool.availableCount()).toBe(poolOptions.min)
expect(localPool.getPoolSize()).toBe(poolOptions.min)
@@ -230,7 +208,7 @@ describe('redisConnectionPool', () => {
await localPool.destroy(client)
await localPool.drain()
- await new Promise((r) => setTimeout(r, 300))
+ await new Promise(r => setTimeout(r, 300))
expect(localPool.availableCount()).toBe(poolOptions.min)
expect(localPool.getPoolSize()).toBe(0)
})
diff --git a/lib/redisStore.js b/lib/redisStore.js
index 5d4a504..ad76cca 100644
--- a/lib/redisStore.js
+++ b/lib/redisStore.js
@@ -14,238 +14,157 @@ const genRandomStr = require('./genRandomStr')
* @param {object} options.logger - Inject your custom logger
* @param {integer} options.ttlInSeconds - Number of seconds to store by default
*/
-const RedisStore = (module.exports = function ({
- name,
- redisOptions,
- poolOptions,
- logger,
- ttlInSeconds
-} = {}) {
- this.name = name || `redisStore-${genRandomStr()}`
- this.redisOptions = redisOptions
- this.poolOptions = poolOptions || {}
- this.logger = createLogger(logger)
- this.ttlInSeconds = ttlInSeconds
-
- this.pool = null
- try {
- this.pool = new RedisPool({
- name: this.name,
- redisOptions: this.redisOptions,
- poolOptions: this.poolOptions,
- logger: this.logger
- })
+module.exports = class RedisStore extends RedisPool {
+ constructor({ name, redisOptions, poolOptions, logger, ttlInSeconds } = {}) {
+ const options = {
+ name: name || `redisStore-${genRandomStr()}`,
+ redisOptions,
+ poolOptions,
+ logger: createLogger(logger),
+ ttlInSeconds
+ }
- // // since pool factory events are not triggered due to retry issue; a workaround
- // this.testConnection()
- // .then((res) => {
- // console.log("#########################", res)
- // debug("Redis store created.", this.pool.status())
- // });
- // this.pool.acquire()
- } catch (e) {
- debug('Failed to create', e)
- this.pool = null
- throw e
+ super(options)
}
-})
-RedisStore.prototype.testConnection = async function () {
- debug('PING to test connection')
+ /**
+ * Returns 'PONG'
+ *
+ * @param {string} str - string passed
+ * @returns {string} 'PONG'/string passed
+ */
+ ping(str) {
+ return this.sendCommand('ping', str)
+ }
- let result
- try {
- result = await this.ping()
- if (result !== 'PONG') {
- debug('expected PONG but got', result)
- const err = new Error('UNKNOWN_PING_RESPONSE')
- err.message = `expected PONG but got : ${result}`
- throw err
+ /**
+ * Returns value or null when the key is missing - See [redis get]{@link https://redis.io/commands/get}
+ *
+ * @param {string} key - key for the value stored
+ * @returns {string} value or null when the key is missing
+ */
+ async get(key) {
+ let result = await this.sendCommand('get', key)
+
+ try {
+ result = JSON.parse(result)
+ } catch (e) {
+ // do nothing
}
- } catch (error) {
- debug('Failed to PING', error)
- this.logger.error('Test connection failed', error)
- throw error
+ return result
}
- return result
-}
-
-/**
- * Returns factory.name for this pool
- *
- * @returns {string} Name of the pool
- */
-RedisStore.prototype.getName = function () {
- return this.pool.getName()
-}
-
-/**
- * Returns this.redisOptions for this pool
- *
- * @returns {object} redis options given
- */
-RedisStore.prototype.getRedisOptions = function () {
- return this.pool.getRedisOptions()
-}
-
-/**
- * Returns this.poolOptions for this pool
- *
- * @returns {object} pool options given
- */
-RedisStore.prototype.getPoolOptions = function () {
- return this.pool.getPoolOptions()
-}
-/**
- * Send redis instructions
- *
- * @param {string} commandName - Name of the command
- * @param {array} commandArgs - Args sent to the command
- * @returns {promise} Promise resolve with the result or Error
- */
-RedisStore.prototype.sendCommand = function (...args) {
- return this.pool.sendCommand.apply(this, args)
-}
-
-/**
- * Returns 'PONG'
- *
- * @param {string} str - string passed
- * @returns {string} 'PONG'/string passed
- */
-RedisStore.prototype.ping = function (str) {
- if (str) {
- return this.sendCommand('ping', str)
+ /**
+ * Returns 'OK' if successful
+ *
+ * @param {string} key - key for the value stored
+ * @param {any} value - value to stored
+ * @param {number} ttlInSeconds - time to live in seconds
+ * @returns {string} 'OK' if successful
+ */
+ set(key, value, ttlInSeconds) {
+ const str =
+ Array.isArray(value) || isJSON(value, true)
+ ? JSON.stringify(value)
+ : value
+ const ttlInS = ttlInSeconds || this.ttlInSeconds
+
+ if (ttlInS) {
+ return this.sendCommand('setex', [key, ttlInS, str])
+ }
+ return this.sendCommand('set', [key, str])
}
- return this.sendCommand('ping')
-}
-/**
- * Returns value or null when the key is missing - See [redis get]{@link https://redis.io/commands/get}
- *
- * @param {string} key - key for the value stored
- * @returns {string} value or null when the key is missing
- */
-RedisStore.prototype.get = async function (key) {
- let result = await this.sendCommand('get', key)
+ /**
+ * Returns 'OK' if successful
+ *
+ * @param {string} key - key for the value stored
+ * @param {any} value - value to stored
+ * @param {number} ttlInSeconds - time to live in seconds
+ * @returns {any}
+ */
+ async getset(key, value, ttlInSeconds) {
+ const str =
+ Array.isArray(value) || isJSON(value, true)
+ ? JSON.stringify(value)
+ : value
+ const ttlInS = ttlInSeconds || this.ttlInSeconds
+
+ let result = await this.sendCommand('getset', [key, str])
+ try {
+ result = JSON.parse(result)
+ } catch (e) {
+ // do nothing
+ }
- try {
- result = JSON.parse(result)
- } catch (e) {
- // do nothing
+ if (ttlInS) {
+ await this.sendCommand('expire', [key, ttlInS])
+ }
+ return result
}
- return result
-}
-
-/**
- * Returns 'OK' if successful
- *
- * @param {string} key - key for the value stored
- * @param {string} value - value to stored
- * @param {number} ttlInSeconds - time to live in seconds
- * @returns {string} 'OK' if successful
- */
-RedisStore.prototype.set = function (key, value, ttlInSeconds) {
- const str =
- Array.isArray(value) || isJSON(value, true) ? JSON.stringify(value) : value
- const ttlInS = ttlInSeconds || this.ttlInSeconds
- if (ttlInS) {
- return this.sendCommand('setex', [key, ttlInS, str])
+ /**
+ * Returns the number of keys that were removed - See [redis del]{@link https://redis.io/commands/del}
+ *
+ * @param {array} keys - list of keys to delete
+ * @returns {number} The number of keys that were removed.
+ */
+ del(keys) {
+ return this.sendCommand('del', keys)
}
- return this.sendCommand('set', [key, str])
-}
-
-RedisStore.prototype.getset = async function (key, value, ttlInSeconds) {
- const str =
- Array.isArray(value) || isJSON(value, true) ? JSON.stringify(value) : value
- const ttlInS = ttlInSeconds || this.ttlInSeconds
- let result = await this.sendCommand('getset', [key, str])
- try {
- result = JSON.parse(result)
- } catch (e) {
- // do nothing
+ /**
+ * Returns 1 if the timeout was set/ 0 if key does not exist or the timeout could not be set - See [redis expire]{@link https://redis.io/commands/expire}
+ *
+ * @param {string} key - key to set expire
+ * @param {number} ttlInSeconds - time to live in seconds
+ * @returns {number} 1 if the timeout was set successfully; if not 0
+ */
+ expire(key, ttlInSeconds) {
+ return this.sendCommand('expire', [key, ttlInSeconds])
}
- if (ttlInS) {
- await this.sendCommand('expire', [key, ttlInS])
+ /**
+ * Returns TTL in seconds, or a negative value in order to signal an error - See [redis ttl]{@link https://redis.io/commands/ttl}
+ *
+ * @param {string} key - list of keys to delete
+ * @returns {number} TTL in seconds, or a negative value in order to signal an error
+ */
+ getTtl(key) {
+ return this.sendCommand('ttl', key)
}
- return result
-}
-/**
- * Returns the number of keys that were removed - See [redis del]{@link https://redis.io/commands/del}
- *
- * @param {array} keys - list of keys to delete
- * @returns {number} The number of keys that were removed.
- */
-RedisStore.prototype.del = function (keys) {
- return this.sendCommand('del', keys)
-}
-
-/**
- * Returns 1 if the timeout was set/ 0 if key does not exist or the timeout could not be set - See [redis expire]{@link https://redis.io/commands/expire}
- *
- * @param {string} key - key to set expire
- * @param {number} ttlInSeconds - time to live in seconds
- * @returns {number} 1 if the timeout was set successfully; if not 0
- */
-RedisStore.prototype.expire = function (key, ttlInSeconds) {
- return this.sendCommand('expire', [key, ttlInSeconds])
-}
-
-/**
- * Returns TTL in seconds, or a negative value in order to signal an error - See [redis ttl]{@link https://redis.io/commands/ttl}
- *
- * @param {string} key - list of keys to delete
- * @returns {number} TTL in seconds, or a negative value in order to signal an error
- */
-RedisStore.prototype.getTtl = function (key) {
- return this.sendCommand('ttl', key)
-}
-
-/**
- * Returns all keys matching pattern - See [redis keys]{@link https://redis.io/commands/keys}
- *
- * @param {string} pattern - glob-style patterns/default '*'
- * @returns {array} all keys matching pattern
- */
-RedisStore.prototype.keys = function (pattern) {
- return this.sendCommand('keys', pattern || '*')
-}
-
-/**
- * Deletes all keys matching pattern
- *
- * @param {string} pattern - glob-style patterns/default '*'
- * @returns {number} The number of keys that were removed.
- */
-RedisStore.prototype.deleteAll = function (pattern) {
- debug('clearing redis keys: ', pattern || '*')
- return this._executeDeleteAll(pattern || '*')
-}
+ /**
+ * Returns all keys matching pattern - See [redis keys]{@link https://redis.io/commands/keys}
+ *
+ * @param {string} pattern - glob-style patterns/default '*'
+ * @returns {array} all keys matching pattern
+ */
+ keys(pattern) {
+ return this.sendCommand('keys', pattern || '*')
+ }
-/**
- * Returns pool status and stats
- *
- * @returns {object} pool status and stats
- */
-RedisStore.prototype.status = function () {
- return this.pool.status()
-}
+ /**
+ * Deletes all keys matching pattern
+ *
+ * @param {string} pattern - glob-style patterns/default '*'
+ * @returns {number} The number of keys that were removed.
+ */
+ deleteAll(pattern) {
+ debug('clearing redis keys: ', pattern || '*')
+ return this._executeDeleteAll(pattern || '*')
+ }
-/**
- * Preloads delete all scripts into redis script cache
- * (this script requires redis >= 4.0.0)
- * @async
- * @returns {Promise} sha1 hash of preloaded function
- * @private
- */
-RedisStore.prototype._loadDeleteAllScript = function () {
- if (!this.__deleteScriptPromise) {
- const deleteKeysScript = `
+ /**
+ * Preloads delete all scripts into redis script cache
+ * (this script requires redis >= 4.0.0)
+ * @async
+ * @returns {Promise} sha1 hash of preloaded function
+ * @private
+ */
+ _loadDeleteAllScript() {
+ if (!this.__deleteScriptPromise) {
+ const deleteKeysScript = `
local keys = {};
local done = false;
local cursor = "0";
@@ -263,32 +182,33 @@ RedisStore.prototype._loadDeleteAllScript = function () {
end
until done
return deleted;`
- this.__deleteScriptPromise = this.sendCommand('SCRIPT', [
- 'LOAD',
- deleteKeysScript
- ])
+ this.__deleteScriptPromise = this.sendCommand('SCRIPT', [
+ 'LOAD',
+ deleteKeysScript
+ ])
+ }
+ return this.__deleteScriptPromise
}
- return this.__deleteScriptPromise
-}
-/**
- * Preloads and execute delete all script
- * @async
- * @param {string} pattern - glob-style patterns/default '*'
- * @returns {Promise} The number of keys that were removed.
- * @private
- */
-RedisStore.prototype._executeDeleteAll = async function (pattern) {
- let sha1
- try {
- sha1 = await this._loadDeleteAllScript()
- } catch (error) {
- if (error.code === 'NOSCRIPT') {
- // We can get here only if server is restarted somehow and cache is deleted
- this.__deleteScriptPromise = null
- return this._executeDeleteAll(pattern)
+ /**
+ * Preloads and execute delete all script
+ * @async
+ * @param {string} pattern - glob-style patterns/default '*'
+ * @returns {Promise} The number of keys that were removed.
+ * @private
+ */
+ async _executeDeleteAll(pattern) {
+ let sha1
+ try {
+ sha1 = await this._loadDeleteAllScript()
+ } catch (error) {
+ if (error.code === 'NOSCRIPT') {
+ // We can get here only if server is restarted somehow and cache is deleted
+ this.__deleteScriptPromise = null
+ return this._executeDeleteAll(pattern)
+ }
+ throw error
}
- throw error
+ return this.sendCommand('EVALSHA', [sha1, 0, pattern, 1000])
}
- return this.sendCommand('EVALSHA', [sha1, 0, pattern, 1000])
}
diff --git a/lib/redisStore.test.js b/lib/redisStore.test.js
index 0574e18..1903257 100644
--- a/lib/redisStore.test.js
+++ b/lib/redisStore.test.js
@@ -6,15 +6,8 @@ describe('redisStore', () => {
host: process.env.REDIS_HOST || '127.0.0.1',
auth_pass: process.env.REDIS_AUTH
}
- const poolOptions = {
- min: 2,
- max: 4
- }
- const options = {
- name,
- redisOptions,
- poolOptions
- }
+ const poolOptions = { min: 2, max: 4 }
+ const options = { name, redisOptions, poolOptions }
let store
beforeEach(() => {
@@ -99,9 +92,7 @@ describe('redisStore', () => {
test('retrieve parsed json', async () => {
const key = 'chuck-norris'
- const value = {
- type: 'superman'
- }
+ const value = { type: 'superman' }
await store.set(key, value)
@@ -134,9 +125,7 @@ describe('redisStore', () => {
test('store json', async () => {
const key = 'key'
- const value = {
- type: 'json'
- }
+ const value = { type: 'json' }
const result = await store.set(key, value)
expect(result).toBe('OK')
@@ -164,7 +153,7 @@ describe('redisStore', () => {
expect(store.get(key)).resolves.toBe(value)
- await new Promise((r) => setTimeout(r, 1100))
+ await new Promise(r => setTimeout(r, 1100))
expect(store.get(key)).resolves.toBeNull()
})
})
@@ -195,7 +184,7 @@ describe('redisStore', () => {
const result = await store.expire(key, ttlInSeconds)
expect(result).toBe(1)
- await new Promise((r) => setTimeout(r, 1100))
+ await new Promise(r => setTimeout(r, 1100))
expect(store.get(key)).resolves.toBeNull()
})
@@ -220,14 +209,11 @@ describe('redisStore', () => {
})
describe('keys', () => {
- const keyValues = {
- key1: 'value1',
- key2: 'value2'
- }
+ const keyValues = { key1: 'value1', key2: 'value2' }
beforeEach(async () => {
await Promise.all(
- Object.keys(keyValues).map((key) => store.set(key, keyValues[key]))
+ Object.keys(keyValues).map(key => store.set(key, keyValues[key]))
)
})
@@ -242,15 +228,12 @@ describe('redisStore', () => {
})
describe('deleteAll', () => {
- const keyValues = {
- key1: 'value1',
- key2: 'value2'
- }
+ const keyValues = { key1: 'value1', key2: 'value2' }
beforeEach(async () => {
await store.deleteAll()
await Promise.all(
- Object.keys(keyValues).map((key) => store.set(key, keyValues[key]))
+ Object.keys(keyValues).map(key => store.set(key, keyValues[key]))
)
})