0
votes

I hope someone can give me some advice

I have created a module, that basically creates a redis connection using a singleton pattern which also uses promises.

However, before I return the redis connection, I first check if the connection is ready, on the ready event, I resolve the promise and likewise on any errors, I reject the promise.

My only concern is with this method, can I introduce memory leaks as the listeners function on ready and error may continue listening well after the promise has completed and should be cleaned up by the garbage collector.

I am not sure if this will create some kind of memory leaks..

Any advice would be much appreciated.

   

    import redis from 'redis';
       import redisearch from 'redis-redisearch';
    
       redisearch(redis);
    
       let redisClient: redis.RedisClient = null;
    
    
       export function getRedisClient(): Promise {
    
    
        return new Promise((resolve: any, reject: any) => {
    
            if (redisClient && redisClient.connected) {
                return resolve(redisClient);
            }
    
            redisClient = redis.createClient({
    
                password: process.env.REDIS_PASSWORD,
    
                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);
                },
            });
    
            redisClient.on("ready", function (error: any) {
                console.log("connection is good");
                return resolve(redisClient);
            });
    
            redisClient.on("error", function (error: any) {
                console.log("reject error");
                if (redisClient) {
                    redisClient.end(false);
                    redisClient = null;
                    return reject(error);
                }
    
            });
        })
    }

1

1 Answers

1
votes

This pattern can create multiple redisClients if getRedisClient is called multiple times before any of the redisClients finish connecting. A slightly simpler pattern might be to cache a Promise<RedisClient> rather than the redisClient:

let redisClientP: Promise<RedisClient>;
function getRedisClient (): Promise<RedisClient> {
  if (redisClientP) return redisClientP;
  redisClientP = new Promise(...previous code)
  return redisClientP;
}

If you haven't seen this type of cached Promise usage before, here's a small snippet demonstrating that you can access the .then on a Promise multiple times.

const p = new Promise((resolve) => resolve(2));


(async function main () {
  for (let i = 0; i < 100; i++) {
    console.log(i, await p); 
  }
})()