Prevent Redis client from crashing your Node.js app

Posted on 1 Comment
Credit: http://crash.rocks/
Credit: http://crash.rocks/

Redis is a high-performance in-memory database that is often used for data caching on server-side. The redis npm package makes is nice & easy to use Redis to cache data in a Node app. If you are new to Redis, you will find this tutorial on Coligo helpful in implementing data caching in your own application. It often results in 10x or more performance increase. Don’t take my work, check it out yourself!

An annoying issue with the redis package is that, if Redis server is not running, doing this emits an uncaught exception, crashing the whole app:

redisClient = redis.createClient();

As this operation, like most operations in Node, is asynchronous, using try-catch simply doesn’t work.

My solution is to create a singleton Redis client instance that handles the Redis-server-not-running case through its retry_strategy option. When Redis server is not running, my singleton returns a fake Redis client that has same function names as the real Redis client. This gives me the benefit of using redis package’s code the way it’s advertised on its documentation without any additional error handling code and without worrying about whether Redis server is running.

I hope my code helps you too. Just create a file redisClient.js somewhere in your Node app and require it when you need to use it.

var redisClient = require('common/redisClient');
 
...
 
var redis = redisClient.getClient();
redis.setex(unique_key_name, 60, data_to_be_cached);

 

/////////////////////////////////////////////////
// Singleton for Redis cache database client.
//
// @file: redisClient.js
// @author: Anurag Bhandari
/////////////////////////////////////////////////
 
var redis = require('redis');
 
var redisClient = (function () {
 
    // Start with a fake client so that we have a client that works
    // even when Redis server is down
    var client = {
        get: function (key, cb) {
            cb(null, null);
        },
        setex: function (key, time, value) {
            // Do nothing in particular
        }
    };
 
    // Attempt to create a new instance of an actual redis client
    var connectionString = process.env.REDIS_URL || 'redis://localhost:6379';
    var c = redis.createClient(connectionString, {
        retry_strategy: function (options) {
            if (options.error.code === 'ECONNREFUSED') {
                // This will suppress the ECONNREFUSED unhandled exception
                // that results in app crash
                return;
            }
        }
    });
 
    // Set the "client" variable to the actual redis client instance
    // once a connection is established with the Redis server
    c.on('ready', function () {
        client = c;
    });
 
    /**
     * Get a redis client
     * @return {Object} client - eventually a proper redis client object (if redis is up) or a fake client object (if redis is down)
     */
    var getClient = function () {
        return client;
    };
 
    return {
        getClient: getClient
    }
 
})();
 
module.exports = redisClient;