Created
January 11, 2018 17:13
-
-
Save giles-v/942598b8e4e19beab035270b2cc7e791 to your computer and use it in GitHub Desktop.
Redis caching module which can reconnect if the connection drops
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import redis from 'redis'; | |
const RECONNECT_TIME_THRESHOLD = 10000; | |
const keyNotFoundError = new Error('Key not found'); | |
keyNotFoundError.keyNotFound = true; | |
const internals = { | |
redis, | |
reconnectTimeout: null, | |
client: null, | |
error: new Error('Server not yet ready'), | |
info: msg => { | |
if (process.env.NODE_ENV === 'test') { | |
return; | |
} | |
console.log(msg); | |
} | |
}; | |
/** | |
* Creates a new instance of the Redis client, and attaches listeners to | |
* it. On error events, it will queue up regeneration of the client (calling itself). | |
* | |
* @returns {Object} Redis client instance | |
*/ | |
internals.createClient = () => { | |
const opts = { | |
host: process.env.REDIS_HOST, | |
port: process.env.REDIS_PORT || '6379', | |
connect_timeout: 3000 | |
}; | |
const client = internals.redis.createClient(opts); | |
client.on('end', function () { | |
internals.info('Redis cache: connection ended.'); | |
}); | |
client.on('error', function (err) { | |
internals.client.quit(); | |
if (internals.error && internals.reconnectTimeout) { | |
// we've already logged the issue, and are trying to reconnect. | |
return; | |
} | |
logger.error(err); | |
internals.error = err; | |
internals.reconnectTimeout = setTimeout(() => { | |
internals.info('Redis cache: attempting to reconnect...'); | |
internals.client = internals.createClient(); | |
internals.reconnectTimeout = null; | |
}, RECONNECT_TIME_THRESHOLD); | |
}); | |
client.on('ready', function () { | |
internals.info('Redis cache: connection ready.'); | |
internals.error = null; | |
}); | |
return client; | |
}; | |
/** | |
* Initialize (make initial connection) | |
* | |
* @returns {undefined} undefined | |
*/ | |
export const init = () => { | |
if (internals.client) { | |
return; | |
} | |
internals.client = internals.createClient(); | |
}; | |
/** | |
* Set a value in the Redis cache. | |
* | |
* @param {string} key - data key | |
* @param {Object} value - data to store | |
* @param {number} ttl - expiry for the data. Pass 0 or null for no expiry. | |
* @param {Function} callback - function to call with an error or response | |
* @returns {undefined} undefined | |
*/ | |
export const set = (key, value, ttl, callback) => { | |
const valueString = JSON.stringify(value); | |
internals.info(`Redis cache: setting key '${key}', data length ${valueString.length}`); | |
if (internals.error) { | |
return callback(internals.error); | |
} | |
if (ttl > 0) { | |
return internals.client.set(key, valueString, 'EX', ttl, callback); | |
} | |
internals.client.set(key, valueString, callback); | |
}; | |
/** | |
* Get a value from the Redis cache. | |
* | |
* @param {string} key - data key | |
* @param {Function} callback - function to call with an error or response | |
* @returns {undefined} undefined | |
*/ | |
export const get = (key, callback) => { | |
if (internals.error) { | |
internals.info(`Redis cache: attempting to get key '${key}'`); | |
return callback(internals.error); | |
} | |
internals.client.get(key, (err, reply) => { | |
if (err) { | |
return callback(err); | |
} | |
if (reply === null) { | |
internals.info(`Redis cache: key '${key}' not found`); | |
return callback(keyNotFoundError); | |
} | |
internals.info(`Redis cache: got key '${key}' with data length ${reply.length}`); | |
return callback(err, JSON.parse(reply)); | |
}); | |
}; | |
/** | |
* Flush all keys from Redis. | |
* | |
* @param {Function} callback - function to call with an error or response | |
* @returns {undefined} undefined | |
*/ | |
export const flushAll = callback => { | |
if (internals.error) { | |
return callback(internals.error); | |
} | |
internals.info('Redis cache: flushAll'); | |
internals.client.flushall(callback); | |
}; | |
/** | |
* Quit the Redis session. | |
* | |
* @returns {undefined} undefined | |
*/ | |
export const quit = () => { | |
internals.info('Redis cache: quit'); | |
internals.client.quit(); | |
}; | |
export const __internals__ = internals; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment