import { RedisClientType, RedisClusterType, createCluster, createClient } from 'redis' import { CacheConfig } from './types.js' import AsyncSingleton from '../Util/AsyncSingleton.js' /** * Represents a Redis cache connection class. */ export default class Redis { private static singletonMap = new Map< string, AsyncSingleton> >() /** * A private static property that holds a reference to the redis.createClient function. * This property is used to create client. */ private static ClientFactory = createClient /** * A private static property that references the createCluster function. * This property can be used to create clusters within the class. */ private static ClusterFactory = createCluster public static connection( config: CacheConfig<'redis'> ): Promise { const key = Redis.cacheKey(config) let singleton = Redis.singletonMap.get(key) if (!singleton) { singleton = new AsyncSingleton>( Redis.connectionFactory, async c => c.isOpen ) Redis.singletonMap.set(key, singleton) } return singleton.instance(config) } private static clearSingletons(): void { for (const singleton of Redis.singletonMap.values()) { if (singleton.getValue()) singleton.clear() } Redis.singletonMap.clear() } private static cacheKey(config: CacheConfig<'redis'>): string { return JSON.stringify({ type: config.type, hostname: config.hostname, port: Number(config.port ?? 6379), username: config.username, password: config.password ?? null, enableTLS: Boolean(config.enableTLS), clusterMode: Boolean(config.clusterMode), }) } private static async connectionFactory( config: CacheConfig<'redis'> ): Promise { if (config.clusterMode) return Redis.redisClusterConnection(config) else return Redis.redisClientConnection(config) } /** * Establishes a connection to a Redis client based on the provided configuration * and holds it for reusability. If connection is detected closed, new connection is * initialized and established. * @param {CacheConfig<'redis'>} config - The configuration object for connecting to the Redis client. * @returns {Promise} A promise that resolves to the Redis client connection. */ private static async redisClientConnection( config: CacheConfig<'redis'> ): Promise { console.debug('Starting remote client cache connection') const connection = Redis.ClientFactory({ username: config.username, ...(config.password ? { password: config.password } : {}), disableOfflineQueue: true, socket: { port: Number(config.port ?? 6379), host: config.hostname, tls: config.enableTLS, connectTimeout: 10000, }, }) await connection.connect() return connection as any } private static async redisClusterConnection( config: CacheConfig<'redis'> ): Promise { console.debug('Starting remote cluster cache connection') const connection = Redis.ClusterFactory({ defaults: { username: config.username, ...(config.password ? { password: config.password } : {}), socket: { tls: config.enableTLS, connectTimeout: 10000, }, }, rootNodes: [ { url: `redis://${config.username}:${config.username}@${config.hostname}:${Number(config.port ?? 6379)}`, disableOfflineQueue: true, }, ], }) await connection.connect() return connection as any } }