From 86b5281e8624d309d60d9a63f6d2bd0b35b6aecc Mon Sep 17 00:00:00 2001 From: Charlie Lamb Date: Wed, 22 Apr 2026 10:38:09 +0100 Subject: [PATCH] chore: no flakely errors on redis --- server/src/utils/cacheUtils/cacheUtils.ts | 15 ++++++++++++--- server/tests/unit/cache/cache-utils.test.ts | 12 ++++++++++++ 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/server/src/utils/cacheUtils/cacheUtils.ts b/server/src/utils/cacheUtils/cacheUtils.ts index a69401e46..e036381a9 100644 --- a/server/src/utils/cacheUtils/cacheUtils.ts +++ b/server/src/utils/cacheUtils/cacheUtils.ts @@ -14,6 +14,12 @@ const markDefaultRedisAvailability = (targetRedis: Redis, available: boolean) => available ? markRedisCommandSuccess() : markRedisCommandFailure(); }; +const isRedisAvailabilityError = (targetRedis: Redis, error: unknown) => { + if (targetRedis.status !== "ready") return true; + const message = error instanceof Error ? error.message : String(error); + return /ECONN|ETIMEDOUT|timeout|closed|writeable|max retries/i.test(message); +}; + const warnRedisUnavailable = ({ source, error, @@ -68,7 +74,8 @@ export const tryRedisNx = async ({ if (result === "OK") return await onSuccess(); return await onKeyAlreadyExists(); } catch (error) { - markDefaultRedisAvailability(targetRedis, false); + if (isRedisAvailabilityError(targetRedis, error)) + markDefaultRedisAvailability(targetRedis, false); warnRedisUnavailable({ source: "tryRedisNx:error", error }); return await onRedisUnavailable(); } @@ -103,7 +110,8 @@ export const tryRedisWrite = async ( ? true : T | null; } catch (error) { - markDefaultRedisAvailability(targetRedis, false); + if (isRedisAvailabilityError(targetRedis, error)) + markDefaultRedisAvailability(targetRedis, false); warnRedisUnavailable({ source: "tryRedisWrite:error", error }); return null as T extends void ? true : T | null; } @@ -134,7 +142,8 @@ export const tryRedisRead = async ( markDefaultRedisAvailability(targetRedis, true); return result; } catch (error) { - markDefaultRedisAvailability(targetRedis, false); + if (isRedisAvailabilityError(targetRedis, error)) + markDefaultRedisAvailability(targetRedis, false); warnRedisUnavailable({ source: "tryRedisRead:error", error }); return null; } diff --git a/server/tests/unit/cache/cache-utils.test.ts b/server/tests/unit/cache/cache-utils.test.ts index e39273836..e49d4d23d 100644 --- a/server/tests/unit/cache/cache-utils.test.ts +++ b/server/tests/unit/cache/cache-utils.test.ts @@ -139,4 +139,16 @@ describe("cache utils", () => { expect(getRedisAvailability().state).toBe("healthy"); }); + + test("Redis command errors do not mark Redis unavailable", async () => { + markRedisCommandSuccess(); + markRedisCommandSuccess(); + expect(getRedisAvailability().state).toBe("healthy"); + + await tryRedisWrite(async () => { + throw new Error("ERR user_script:2: unexpected symbol near '#'"); + }); + + expect(getRedisAvailability().state).toBe("healthy"); + }); });