diff --git a/server/iocContainer/index.ts b/server/iocContainer/index.ts index 739571a..04f009a 100644 --- a/server/iocContainer/index.ts +++ b/server/iocContainer/index.ts @@ -579,6 +579,22 @@ export default async function getBottle() { }), ); + // AUTH-enabled Redis (e.g. ElastiCache with an auth token) rejects every + // command with NOAUTH unless credentials are sent, which leaves ioredis stuck + // before "ready" and parks commands in the offline queue forever. Local dev + // Redis has no password, so only pass credentials when REDIS_PASSWORD is set; + // REDIS_USER may be set-but-empty, which means the default user. Shared by the + // cluster and single-node paths so both authenticate identically. + const redisAuthOptions = (): { username?: string; password?: string } => + process.env.REDIS_PASSWORD + ? { + ...(process.env.REDIS_USER + ? { username: process.env.REDIS_USER } + : {}), + password: process.env.REDIS_PASSWORD, + } + : {}; + const makeRedis = ( extraOptions: { enableOfflineQueue?: boolean } = {}, ): IORedis.Redis | Cluster => @@ -599,8 +615,7 @@ export default async function getBottle() { // Required by BullMQ: its workers use blocking Redis commands // that would otherwise be misinterpreted as timed-out requests. maxRetriesPerRequest: null, - username: safeGetEnvVar('REDIS_USER'), - password: safeGetEnvVar('REDIS_PASSWORD'), + ...redisAuthOptions(), ...extraOptions, }, }, @@ -611,6 +626,7 @@ export default async function getBottle() { maxRetriesPerRequest: null, port: parseInt(process.env.REDIS_PORT ?? '6379'), host: safeGetEnvVar('REDIS_HOST'), + ...redisAuthOptions(), ...(isEnvTrue('REDIS_TLS') ? { tls: { servername: safeGetEnvVar('REDIS_HOST') } } : {}),