Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Redis client - on('ready') event

I am using the Redis NPM library for Node.js - I can listen for the connection 'ready' event like so

var client = require('redis').createClient();

client.on('ready', function () {
    client.keys('*', function (err, keys) {
        if (err) {
            log.error(err);
        } else {
            for (var i = 0; i < keys.length; i++) {
                createLocalDataMap(keys[i]);
            }
        }
    });
});

however, how can I query Redis to see if it's ready instead of listening for an event that will probably fire before I am ready to handle it?

I suppose I could abandon the ready event and just do a query and then wait for a response, but is there a better, safer and more sophisticated way?

In other words, in all likelihood I will have to write a function like this:

var isReady = false;

client.on('ready', function(){

      isReady = true;
});

function doSomethingMuchLater(){

if(isReady){

     //query redis as normal
}
else {

   client.on('ready', function(){
   isReady = true;
     //now I do what I wanted to do
   });

}
}

this does not seem right at all, there must be a better way

like image 779
Alexander Mills Avatar asked Apr 01 '26 04:04

Alexander Mills


2 Answers

The node-redis client automatically queues any commands you send before the connection is ready. Once it connects, those commands all get sent just before .on('ready') fires. As such, you really don't need to use .on('ready'). If you send commands too early, they'll still go through as expected, and if your connection fails entirely, they'll never be sent.

like image 130
Ben Fried Avatar answered Apr 02 '26 20:04

Ben Fried


Even though the commands are queued, I got some nasty logging errors because I sending commands while the DB was not fully loaded. The connect event did not suffice.

From the docs:

[options.enableReadyCheck] boolean

When a connection is established to the Redis server, the server might still be loading the database from disk. While loading, the server not respond to any commands. To work around this, when this option is true, ioredis will check the status of the Redis server, and when the Redis server is able to process commands, a ready event will be emitted.

So this is the only way I could make it reliably work is by using the ioredis library:

import Redis from 'ioredis';

class RedisClient {
  private static instance: Redis.Redis | undefined;
  private redisUrl: string;
  private options?: Redis.RedisOptions;

  private constructor(
    redisUrl?: string,
    options?: Redis.RedisOptions,
  ) {
    this.redisUrl = redisUrl || `redis://${REDIS.HOST}:${REDIS.PORT}`;
    this.options = options;
  }

  static async init(
    redisUrl?: string,
    options?: Redis.RedisOptions,
  ): Promise<RedisClient | undefined> {
    try {
      RedisClient.instance = new Redis(RedisClient.redisUrl, {
        ...options,
        enableReadyCheck: true,
      });
      const onReady = (): Promise<boolean> => new Promise((resolve) => {
        (RedisClient.instance as Redis.Redis).on('ready', () => {
          resolve(true);
          infoLogger.info('Redis server online...');
        });
      });
      await onReady();
      return new RedisClient(redisUrl, {
        ...options,
        enableReadyCheck: true
      });
    } catch (err) {
      new ServerError({
        message: 'Redis client init error.',
        internalCode: ErrorCodes.cacheInitError,
        internal: true,
      }).log();

      if (RedisClient.instance) RedisClient.instance.quit();
      return undefined;
    }
  }

  async get(key: string): Promise<someType> {
    ...
  }

  async set<T extends Record<string, unknown>>(key: string, value: Stringified<T> | string): Promise<void> {
    ...
  }

  async del(key: string): Promise<void> {
    ...
  }

  async flush(): Promise<void> {
    ...
  }
}

import RedisClient from 'somewhere';

const redis = await RedisClient.init();
like image 23
Omar Omeiri Avatar answered Apr 02 '26 20:04

Omar Omeiri