mouchin777
mouchin777

Reputation: 1588

How to handle a redis connection error in express?

I have an express database that connects to multiple redis servers by receiving the connection ip from a client.

It works fine if the redis database exists. The problem im facing is that if the ip doesnt exist, I will receive a

Error: Error: Redis connection to xxxx failed - connect ECONNREFUSED xxx

Being this totally understandable, but the problem is that my connection object is still defined. And i cant really seem how to identify this object as a failed connection. The only hint i might have might be in the connection._events.error variable, but it returns a [Function]

This is my code

let connections = []

const killRedis = (redisClient, ip, port) => {
    redisClient.quit()
    connections = connections.filter((i) => { return i.ip !== ip && i.port != port })
}

const subscribe = (redisClient, url, port) => {
    redisClient.on('error', function (err) {
        //this is supposed to handle the error, but i dont know how to make the "undefined" value available for the createRedisClient function. Since this seems to be delayed
        killRedis(redisClient, url, port)
        return undefined;
    });
    redisClient.on('connect', function () {
        console.log('Connected to Redis');
        return redisClient;
    });
}

findConnection = (ip, port) => {
    let connection = connections.filter(i => i.ip == ip && i.port == port)

    if (connection && connection.length > 0) {

        return connection[0].connection
    } else {
        connections.push({
            ip: ip,
            port: port,
            connection: require('redis').createClient(port, ip, {
                no_ready_check: true,
                db: 1
            })
        })
        let pushedConnection = connections.filter(i => i.ip == ip && i.port == port)[0].connection
        subscribe(pushedConnection, ip, port)
        return pushedConnection
    }
}
const createRedisClient = async (port, url) => {
    let redisClient = findConnection(url, port)
    console.log(redisClient._events.error) //[Function]
    return redisClient // this is always a connection object, even if the connection is faulty
}
module.exports = { createRedisClient }

Upvotes: 0

Views: 2105

Answers (1)

Riwen
Riwen

Reputation: 5190

Connection status is not known then findConnection returns, because node-redis is asynchronous, it uses callbacks. One solution would be to promisify your subscribe function, like this:

const subscribe = (redisClient, url, port) => new Promise((resolve, reject) => {
    redisClient.on('error', function (err) {
        console.log(err)
        killRedis(redisClient, url, port)
        reject(err)
    });
    redisClient.on('connect', function () {
        console.log('Connected to Redis');
        resolve(redisClient)
    });
})

Then, in the findConnection, you just handle the possible rejections with a try-catch block. Note that it's been marked async:

findConnection = async (ip, port) =>  {
    let connection = connections.filter(i => i.ip == ip && i.port == port)

    if (connection && connection.length > 0) {

        return connection[0].connection
    } else {
        connections.push({
            ip: ip,
            port: port,
            connection: require('redis').createClient(port, ip, {
                no_ready_check: true,
                db: 1
            })
        })
        let pushedConnection = connections.filter(i => i.ip == ip && i.port == port)[0].connection
        try {
            const client = await subscribe(pushedConnection, ip, port)
            return client
        } catch (error) {
            return undefined // not sure what you want to do with rejections
        }
    }

Then, finally (also async):

const createRedisClient = async (port, url) => {
    let redisClient = await findConnection(url, port)
    console.log(redisClient)
    return redisClient // this is undefined if there was an error connecting 
}

Instead of async-await, you could always use then-catch, etc. Also, I believe the error callback will be called on every error. That is, in case of a successful connection, if you encounter any redis errors later on, killRedis will still be called. If it's intended, alright. Otherwise, you may want to refactor your logic so that connections are only registered when they have successfully been made.

Upvotes: 1

Related Questions