How to configure Node Redis client to throw errors immediately, when connection has failed? [READ DETAILS]

I am using Node Redis with this config:

import redis from "redis";
import { promisify } from "util";
import config from "../../appConfig";

const redisUrl = config.REDIS_URL;
const [host, port] = redisUrl.substr(8).split(":");
const RedisClient = redis.createClient({ host: host, port: Number(port) });
RedisClient.on("error", (err) => {
    logger.error(err);
});
const GetAsync = promisify(RedisClient.get).bind(RedisClient);
const SetAsync = promisify(RedisClient.set).bind(RedisClient);

export { RedisClient, GetAsync, SetAsync };

I have some code which first tries reading from Redis and if that fails, tries reading from MongoDB inside a catch block. Something like this:

try {
    userId = await GetAsync(idKeyStr);
} catch(err) {
    userId = await GetFromMongoDB();
}

My problem is that when the connection fails, the code just gets stuck at userId = await GetAsync(idKeyStr). No exception is being thrown so that the code moves into the catch block. The default time till which the client attempts to reconnect is 1 hour. If within that 1 hour, the server comes up, it will reconnect and the request which was stuck will finally get handled. If it doesnt come up, that request will just be stuck for 1 hour.

If I throw an error from the callback for error event, the application just stops, because of unhandled exception.

I found the retry_strategy option for the createClient method here: https://github.com/NodeRedis/node-redis This is the example given on that page:

const client = redis.createClient({
  retry_strategy: function(options) {
    if (options.error && options.error.code === "ECONNREFUSED") {
      // End reconnecting on a specific error and flush all commands with
      // a individual error
      return new Error("The server refused the connection");
    }
    if (options.total_retry_time > 1000 * 60 * 60) {
      // End reconnecting after a specific timeout and flush all commands
      // with a individual error
      return new Error("Retry time exhausted");
    }
    if (options.attempt > 10) {
      // End reconnecting with built in error
      return undefined;
    }
    // reconnect after
    return Math.min(options.attempt * 100, 3000);
  },
});

To throw an error immediately, I modified this to:

const client = redis.createClient({
      retry_strategy: function() {
          return new Error("The server refused the connection");
      },
    });

But this stops the retries altogether, so even when the server comes up, the client has stopped connection attempts so it never reconnects.

How can I configure it so that the client continues to reconnect but any issued commands fail fast, so that my other code continues execution?

Upvotes: 1

Views: 5789

Answers (2)

stick
stick

Reputation: 88

For anyone using this in 2022, the configuration name is now disableOfflineQueue. It is set to false by default, so you have to set it to true in creatClient.

This is how I do it in my code

import { createClient } from 'redis';

const client = createClient({
    url: redisHost, // redisHost is something like redis://:@localhost:6379/1
    disableOfflineQueue: true,
});

(async () => {
    await client.connect();
})();

Upvotes: 4

Setting enable_offline_queue to false did the trick. Found lots of similar questions but none mentioned this:

Nodejs set timeout for Redis requests

What's the default timeout of ioredis send command for any redis call

How to set read timeout on node redis client?

Finally got this solution from here: https://github.com/NodeRedis/node-redis/issues/789#issuecomment-373270272

Do keep in mind that, with enable_offline_queue set to false, the commands that you issue while there's some connection issue with the server will never be executed.

Upvotes: 0

Related Questions