node-redis icon indicating copy to clipboard operation
node-redis copied to clipboard

Code stuck on connect if using reconnectStrategy

Open cattermo opened this issue 2 years ago • 3 comments

I would like to build my express server independent on weather the redis client is up or not. If redis is down, it should off course try to reconnect, but nothing should fail in the meantime.

When I start the express server I want to try to connect to redis, but move on (and reconnect in background) if it can't connect. I thought this could be easily done by using try {} catch {} around connect call together with reconnectStrategy. Turns out code gets stuck at connect and will never move to next step. Is there a way to make this work? Or is it a bug that the code will not move to next line?

If I remove client.on("error", ...) the code will move on but the reconnect will not happen.

const client = createClient({
      socket: {
        host: config.host,
        port: config.port,
        reconnectStrategy: () => 1000 * 10,
      },
    });
client.on("error", (err) => console.log("Redis error", err));

try {
  await client.connect
} catch (err) {
  console.log("Error from redis connect, "err); // only get here if not using client.on("error",...)
}
console.log("next step in startup..."); // I only get to here when reconnectStrategy is off or I remove client.on("error", ...)

Environment:

  • Node.js Version: 16
  • Redis Server Version: 6
  • Node Redis Version: ^4.0.6
  • Platform: node:16.14.0-alpine3.15 (docker)

cattermo avatar May 03 '22 14:05 cattermo

I ran into the same problem. There is one solution I found: https://stackoverflow.com/questions/64034553/nodejs-redis-reconnect-in-background

Though, I don't think it is the ideal way. Is there a solution how to achieve it without writing proxy class?

Btw, It looks like background reconnection is working in v4. UPDATE. It doesn't work in v4 out of the box. I am doing this:

let redis = {
    client: null,
    isConnected: () => false,
}

client.on('ready', () => {
      console.log('ready')
      redis.isConnected = () => true;
    });
client.on('error', err => {
      console.log(err);
      redis.isConnected = () => false;
});
.....
// Later when I use Get or Set
// I check for connection first

if(!redis.isConnected()) return;

redis.client.get(...)

michalica avatar May 04 '22 07:05 michalica

I am doing this:

        let client = ...
        let isFirst = true;
        let connTask = new Promise<void>(async (resolve) => {
            client.on('error', async (error) => {
                if (isFirst) {
                    //replenish
                    isFirst = false;
                    return resolve();
                }
            });
            client.on('end', async (error) => {
                isFirst = false;
            });
            client.on('ready', async (error) => {
                isFirst = false;
            });
            await client.connect();
            //success!
            return resolve();
        });
        await connTask;

iclouden avatar Jun 06 '22 13:06 iclouden

I am doing this: you must check "isConnected" for queries and u can delete await from "client.connect();"

class RedisConnection{
	client;

	isConnected = false;

	async connect(){
		try {
			this.client = createClient({
				url: redisConfig.REDIS_URI,
				socket: {
					reconnectStrategy: (retries) => {
						if (retries > 50){
							throw new Error('Redis limit retry connection');
						} else if (retries > 25){
							return 30 * 1000;
						} if (retries > 10){
							return 15 * 1000;
						}
						return 10 * 100;
					}
				}
			});

			this.client.on('error', (err) => {
				this.isConnected = false;
				__logger.error(`Redis Error: ${err}`);
			});

			this.client.on('connect', () => {
				this.isConnected = true;
				console.log(`Redis Connected: ${redisConfig.REDIS_URI}`.cyan);
			});

			this.client.connect();
		} catch (err){
			this.isConnected = false;
			__logger.error(`Redis Error: ${err}`);
		}
		return true;
	}
}

firattaskin avatar Sep 02 '22 07:09 firattaskin

@firattaskin & @cattermo Hi everyone. I have tried implementing @firattaskin strategy but when I get an error a retry never occurs. it just errors and then I can't ever have anything happening to get it re-connected. Perhaps it is another issue but I am wondering what I am doing wrong or what else I need to do to get this working.

xtianus79 avatar Oct 05 '22 14:10 xtianus79

If you want to try to reconnect forever but not block the app from starting just don't await to client.connect() (make sure to add a .catch just in case). If you want to retry N times instead just implement a custom reconnect strategy that returns an error when the reconnect should stop (like @firattaskin pointed out in his last comment).

leibale avatar Jan 24 '23 17:01 leibale