import Redis from "ioredis"; let sharedClient: Redis | undefined; let sharedConnection: Promise | undefined; function redisUrl(): string { const value = process.env.REDIS_URL; if (!value) throw new Error("REDIS_URL is required for Redis-backed features."); return value; } export function createRedisClient(connectionName: string): Redis { const client = new Redis(redisUrl(), { connectionName, enableOfflineQueue: false, lazyConnect: true, maxRetriesPerRequest: 2, }); client.on("error", () => undefined); return client; } export async function connectRedisClient(client: Redis): Promise { if (client.status === "ready") return client; if (client.status === "wait") { await client.connect(); return client; } await new Promise((resolve, reject) => { const ready = () => { cleanup(); resolve(); }; const error = (cause: Error) => { cleanup(); reject(cause); }; const cleanup = () => { client.off("ready", ready); client.off("error", error); }; client.once("ready", ready); client.once("error", error); }); return client; } export async function getRedisClient(): Promise { if (!sharedClient) { sharedClient = createRedisClient("buzz-sheet-shared"); } if (!sharedConnection) { sharedConnection = connectRedisClient(sharedClient).catch((cause) => { sharedClient?.disconnect(); sharedClient = undefined; sharedConnection = undefined; throw cause; }); } return sharedConnection; } export async function closeRedisClient(): Promise { const client = sharedClient; sharedClient = undefined; sharedConnection = undefined; if (!client) return; if (client.status === "wait" || client.status === "end") { client.disconnect(); return; } await client.quit().catch(() => client.disconnect()); } export function redisCachePrefix(): string { return process.env.REDIS_CACHE_PREFIX || "buzz:next-cache"; } export function redisCacheTagStateKey(): string { return `${redisCachePrefix()}:tag-timestamps`; } export function redisEventPrefix(): string { return process.env.REDIS_EVENT_PREFIX || "buzz:events"; } export function redisEventChannel(topic: string): string { return `${redisEventPrefix()}:${topic}`; } export function discordWorkerHeartbeatKey(): string { return `${redisEventPrefix()}:discord-worker:heartbeat`; }