diff --git a/package-lock.json b/package-lock.json index 4793763..cde3103 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,7 +21,7 @@ "lz4-napi": "^2.2.0", "pg": "^8.8.0", "rate-limit-redis": "^3.0.1", - "redis": "^4.5.0", + "redis": "^4.6.13", "seedrandom": "^3.0.5" }, "devDependencies": { @@ -1038,19 +1038,19 @@ } }, "node_modules/@redis/bloom": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-1.1.0.tgz", - "integrity": "sha512-9QovlxmpRtvxVbN0UBcv8WfdSMudNZZTFqCsnBszcQXqaZb/TVe30ScgGEO7u1EAIacTPAo7/oCYjYAxiHLanQ==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-1.2.0.tgz", + "integrity": "sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg==", "peerDependencies": { "@redis/client": "^1.0.0" } }, "node_modules/@redis/client": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@redis/client/-/client-1.4.0.tgz", - "integrity": "sha512-1gEj1AkyXPlkcC/9/T5xpDcQF8ntERURjLBgEWMTdUZqe181zfI9BY3jc2OzjTLkvZh5GV7VT4ktoJG2fV2ufw==", + "version": "1.5.14", + "resolved": "https://registry.npmjs.org/@redis/client/-/client-1.5.14.tgz", + "integrity": "sha512-YGn0GqsRBFUQxklhY7v562VMOP0DcmlrHHs3IV1mFE3cbxe31IITUkqhBcIhVSI/2JqtWAJXg5mjV4aU+zD0HA==", "dependencies": { - "cluster-key-slot": "1.1.1", + "cluster-key-slot": "1.1.2", "generic-pool": "3.9.0", "yallist": "4.0.0" }, @@ -1059,33 +1059,33 @@ } }, "node_modules/@redis/graph": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@redis/graph/-/graph-1.1.0.tgz", - "integrity": "sha512-16yZWngxyXPd+MJxeSr0dqh2AIOi8j9yXKcKCwVaKDbH3HTuETpDVPcLujhFYVPtYrngSco31BUcSa9TH31Gqg==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@redis/graph/-/graph-1.1.1.tgz", + "integrity": "sha512-FEMTcTHZozZciLRl6GiiIB4zGm5z5F3F6a6FZCyrfxdKOhFlGkiAqlexWMBzCi4DcRoyiOsuLfW+cjlGWyExOw==", "peerDependencies": { "@redis/client": "^1.0.0" } }, "node_modules/@redis/json": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@redis/json/-/json-1.0.4.tgz", - "integrity": "sha512-LUZE2Gdrhg0Rx7AN+cZkb1e6HjoSKaeeW8rYnt89Tly13GBI5eP4CwDVr+MY8BAYfCg4/N15OUrtLoona9uSgw==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@redis/json/-/json-1.0.6.tgz", + "integrity": "sha512-rcZO3bfQbm2zPRpqo82XbW8zg4G/w4W3tI7X8Mqleq9goQjAGLL7q/1n1ZX4dXEAmORVZ4s1+uKLaUOg7LrUhw==", "peerDependencies": { "@redis/client": "^1.0.0" } }, "node_modules/@redis/search": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@redis/search/-/search-1.1.0.tgz", - "integrity": "sha512-NyFZEVnxIJEybpy+YskjgOJRNsfTYqaPbK/Buv6W2kmFNaRk85JiqjJZA5QkRmWvGbyQYwoO5QfDi2wHskKrQQ==", + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@redis/search/-/search-1.1.6.tgz", + "integrity": "sha512-mZXCxbTYKBQ3M2lZnEddwEAks0Kc7nauire8q20oA0oA/LoA+E/b5Y5KZn232ztPb1FkIGqo12vh3Lf+Vw5iTw==", "peerDependencies": { "@redis/client": "^1.0.0" } }, "node_modules/@redis/time-series": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-1.0.4.tgz", - "integrity": "sha512-ThUIgo2U/g7cCuZavucQTQzA9g9JbDDY2f64u3AbAoz/8vE2lt2U37LamDUVChhaDA3IRT9R6VvJwqnUfTJzng==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-1.0.5.tgz", + "integrity": "sha512-IFjIgTusQym2B5IZJG3XKr5llka7ey84fw/NOYqESP5WUfQs9zz1ww/9+qoz4ka/S6KcGBodzlCeZ5UImKbscg==", "peerDependencies": { "@redis/client": "^1.0.0" } @@ -2058,9 +2058,9 @@ } }, "node_modules/cluster-key-slot": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.1.tgz", - "integrity": "sha512-rwHwUfXL40Chm1r08yrhU3qpUvdVlgkKNeyeGPOxnW8/SyVDvgRaed/Uz54AqWNaTCAThlj6QAs3TZcKI0xDEw==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", + "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==", "engines": { "node": ">=0.10.0" } @@ -4972,16 +4972,16 @@ } }, "node_modules/redis": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/redis/-/redis-4.5.0.tgz", - "integrity": "sha512-oZGAmOKG+RPnHo0UxM5GGjJ0dBd/Vi4fs3MYwM1p2baDoXC0wpm0yOdpxVS9K+0hM84ycdysp2eHg2xGoQ4FEw==", + "version": "4.6.13", + "resolved": "https://registry.npmjs.org/redis/-/redis-4.6.13.tgz", + "integrity": "sha512-MHgkS4B+sPjCXpf+HfdetBwbRz6vCtsceTmw1pHNYJAsYxrfpOP6dz+piJWGos8wqG7qb3vj/Rrc5qOlmInUuA==", "dependencies": { - "@redis/bloom": "1.1.0", - "@redis/client": "1.4.0", - "@redis/graph": "1.1.0", - "@redis/json": "1.0.4", - "@redis/search": "1.1.0", - "@redis/time-series": "1.0.4" + "@redis/bloom": "1.2.0", + "@redis/client": "1.5.14", + "@redis/graph": "1.1.1", + "@redis/json": "1.0.6", + "@redis/search": "1.1.6", + "@redis/time-series": "1.0.5" } }, "node_modules/regexpp": { @@ -6658,43 +6658,43 @@ } }, "@redis/bloom": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-1.1.0.tgz", - "integrity": "sha512-9QovlxmpRtvxVbN0UBcv8WfdSMudNZZTFqCsnBszcQXqaZb/TVe30ScgGEO7u1EAIacTPAo7/oCYjYAxiHLanQ==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-1.2.0.tgz", + "integrity": "sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg==", "requires": {} }, "@redis/client": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@redis/client/-/client-1.4.0.tgz", - "integrity": "sha512-1gEj1AkyXPlkcC/9/T5xpDcQF8ntERURjLBgEWMTdUZqe181zfI9BY3jc2OzjTLkvZh5GV7VT4ktoJG2fV2ufw==", + "version": "1.5.14", + "resolved": "https://registry.npmjs.org/@redis/client/-/client-1.5.14.tgz", + "integrity": "sha512-YGn0GqsRBFUQxklhY7v562VMOP0DcmlrHHs3IV1mFE3cbxe31IITUkqhBcIhVSI/2JqtWAJXg5mjV4aU+zD0HA==", "requires": { - "cluster-key-slot": "1.1.1", + "cluster-key-slot": "1.1.2", "generic-pool": "3.9.0", "yallist": "4.0.0" } }, "@redis/graph": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@redis/graph/-/graph-1.1.0.tgz", - "integrity": "sha512-16yZWngxyXPd+MJxeSr0dqh2AIOi8j9yXKcKCwVaKDbH3HTuETpDVPcLujhFYVPtYrngSco31BUcSa9TH31Gqg==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@redis/graph/-/graph-1.1.1.tgz", + "integrity": "sha512-FEMTcTHZozZciLRl6GiiIB4zGm5z5F3F6a6FZCyrfxdKOhFlGkiAqlexWMBzCi4DcRoyiOsuLfW+cjlGWyExOw==", "requires": {} }, "@redis/json": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@redis/json/-/json-1.0.4.tgz", - "integrity": "sha512-LUZE2Gdrhg0Rx7AN+cZkb1e6HjoSKaeeW8rYnt89Tly13GBI5eP4CwDVr+MY8BAYfCg4/N15OUrtLoona9uSgw==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@redis/json/-/json-1.0.6.tgz", + "integrity": "sha512-rcZO3bfQbm2zPRpqo82XbW8zg4G/w4W3tI7X8Mqleq9goQjAGLL7q/1n1ZX4dXEAmORVZ4s1+uKLaUOg7LrUhw==", "requires": {} }, "@redis/search": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@redis/search/-/search-1.1.0.tgz", - "integrity": "sha512-NyFZEVnxIJEybpy+YskjgOJRNsfTYqaPbK/Buv6W2kmFNaRk85JiqjJZA5QkRmWvGbyQYwoO5QfDi2wHskKrQQ==", + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@redis/search/-/search-1.1.6.tgz", + "integrity": "sha512-mZXCxbTYKBQ3M2lZnEddwEAks0Kc7nauire8q20oA0oA/LoA+E/b5Y5KZn232ztPb1FkIGqo12vh3Lf+Vw5iTw==", "requires": {} }, "@redis/time-series": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-1.0.4.tgz", - "integrity": "sha512-ThUIgo2U/g7cCuZavucQTQzA9g9JbDDY2f64u3AbAoz/8vE2lt2U37LamDUVChhaDA3IRT9R6VvJwqnUfTJzng==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-1.0.5.tgz", + "integrity": "sha512-IFjIgTusQym2B5IZJG3XKr5llka7ey84fw/NOYqESP5WUfQs9zz1ww/9+qoz4ka/S6KcGBodzlCeZ5UImKbscg==", "requires": {} }, "@sinonjs/commons": { @@ -7424,9 +7424,9 @@ } }, "cluster-key-slot": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.1.tgz", - "integrity": "sha512-rwHwUfXL40Chm1r08yrhU3qpUvdVlgkKNeyeGPOxnW8/SyVDvgRaed/Uz54AqWNaTCAThlj6QAs3TZcKI0xDEw==" + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", + "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==" }, "color-convert": { "version": "2.0.1", @@ -9564,16 +9564,16 @@ } }, "redis": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/redis/-/redis-4.5.0.tgz", - "integrity": "sha512-oZGAmOKG+RPnHo0UxM5GGjJ0dBd/Vi4fs3MYwM1p2baDoXC0wpm0yOdpxVS9K+0hM84ycdysp2eHg2xGoQ4FEw==", + "version": "4.6.13", + "resolved": "https://registry.npmjs.org/redis/-/redis-4.6.13.tgz", + "integrity": "sha512-MHgkS4B+sPjCXpf+HfdetBwbRz6vCtsceTmw1pHNYJAsYxrfpOP6dz+piJWGos8wqG7qb3vj/Rrc5qOlmInUuA==", "requires": { - "@redis/bloom": "1.1.0", - "@redis/client": "1.4.0", - "@redis/graph": "1.1.0", - "@redis/json": "1.0.4", - "@redis/search": "1.1.0", - "@redis/time-series": "1.0.4" + "@redis/bloom": "1.2.0", + "@redis/client": "1.5.14", + "@redis/graph": "1.1.1", + "@redis/json": "1.0.6", + "@redis/search": "1.1.6", + "@redis/time-series": "1.0.5" } }, "regexpp": { diff --git a/package.json b/package.json index a6e728f..22c4217 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ "lz4-napi": "^2.2.0", "pg": "^8.8.0", "rate-limit-redis": "^3.0.1", - "redis": "^4.5.0", + "redis": "^4.6.13", "seedrandom": "^3.0.5" }, "devDependencies": { diff --git a/src/utils/redis.ts b/src/utils/redis.ts index 877886c..902bba3 100644 --- a/src/utils/redis.ts +++ b/src/utils/redis.ts @@ -55,9 +55,12 @@ const writeResponseTime: number[] = []; let lastResponseTimeLimit = 0; const maxStoredTimes = 200; +// For redis +let cacheConnectionClientId = ""; + export class TooManyActiveConnectionsError extends Error {} -export let connectionPromise = Promise.resolve(); +export let connectionPromise: Promise = Promise.resolve(); if (config.redis?.enabled) { Logger.info("Connected to redis"); @@ -67,7 +70,7 @@ if (config.redis?.enabled) { void readClient?.connect(); // void as we don't care about the promise exportClient = client as unknown as RedisSB; - const cacheClient = config.redis.clientCacheLength ? createClient(config.redis) : null; + let cacheClient = null as RedisClientType | null; const cache = config.redis.clientCacheLength ? new LRUCache({ max: config.redis.clientCacheLength }) : null; @@ -184,7 +187,7 @@ if (config.redis?.enabled) { Logger.error(`Redis Error: ${error}`); }); /* istanbul ignore next */ - client.on("reconnect", () => { + client.on("reconnecting", () => { Logger.info("Redis: trying to reconnect"); }); /* istanbul ignore next */ @@ -193,26 +196,54 @@ if (config.redis?.enabled) { Logger.error(`Redis Read-Only Error: ${error}`); }); /* istanbul ignore next */ - readClient?.on("reconnect", () => { + readClient?.on("reconnecting", () => { Logger.info("Redis Read-Only: trying to reconnect"); }); - if (cacheClient) { + // It needs to recreate itself when the connection fails as the queue connection doesn't properly restart + const createCacheClient = () => { + cacheClient = createClient(config.redis) as RedisClientType; + /* istanbul ignore next */ - cacheClient.on("error", function(error) { + cacheClient.on("error", function (error) { lastClientFail = Date.now(); Logger.error(`Redis Cache Client Error: ${error}`); }); /* istanbul ignore next */ - cacheClient.on("reconnect", () => { + cacheClient.on("reconnecting", () => { Logger.info("Redis cache client: trying to reconnect"); cache?.clear(); + + void cacheClient.disconnect(); + setTimeout(() => createCacheClient(), 1000); }); - setupClientCache(client as RedisClientType, - readClient as RedisClientType, - cacheClient as RedisClientType, - cache).catch(Logger.error); + // eslint-disable-next-line @typescript-eslint/no-misused-promises + cacheClient.on("ready", async () => { + cache?.clear(); + + await setupCacheClientListener(cacheClient as RedisClientType, cache); + void setupCacheClientTracking(client as RedisClientType, cacheClient as RedisClientType); + void setupCacheClientTracking(readClient as RedisClientType, cacheClient as RedisClientType); + }); + + void cacheClient.connect(); + }; + + if (config.redis.clientCacheLength) { + createCacheClient(); + + client.on("ready", () => { + if (cacheClient.isReady) { + void setupCacheClientTracking(client as RedisClientType, cacheClient as RedisClientType); + } + }); + + readClient?.on("ready", () => { + if (cacheClient.isReady) { + void setupCacheClientTracking(readClient as RedisClientType, cacheClient as RedisClientType); + } + }); } } @@ -237,23 +268,22 @@ export function getRedisStats(): RedisStats { }; } -async function setupClientCache(client: RedisClientType, - readClient: RedisClientType, - cacheClient: RedisClientType, +async function setupCacheClientListener(cacheClient: RedisClientType, cache: LRUCache) { - await cacheClient.connect(); + cacheConnectionClientId = String(await cacheClient.clientId()); - const clientId = await cacheClient.sendCommand(["CLIENT", "ID"]); cacheClient.subscribe("__redis__:invalidate", (messages) => { cache.delete(messages[0]); }).catch(Logger.error); +} - await client.sendCommand(["CLIENT", "TRACKING", "ON", "REDIRECT", String(clientId)]); +async function setupCacheClientTracking(client: RedisClientType, + cacheClient: RedisClientType) { - if (readClient) { - await readClient.sendCommand(["CLIENT", "TRACKING", "ON", "REDIRECT", String(clientId)]); - } + if (!client || !cacheClient.isReady) return; + + await client.sendCommand(["CLIENT", "TRACKING", "ON", "REDIRECT", cacheConnectionClientId]); } export default exportClient;