import Redis, { type Redis as RedisClient, type RedisOptions } from 'ioredis'; const ERR_MISSING_REDIS_URL = 'Missing required Valkey/Redis connection URL. Set VALKEY_URL or REDIS_URL.'; export interface RedisHealthCheck { readonly checkedAt: number; readonly latencyMs: number; readonly ok: boolean; readonly response?: string; readonly error?: string; } export interface RedisPingClient { ping(): Promise; } export type RedisClientConstructor = new ( url: string, options?: RedisOptions, ) => TClient; export interface CreateRedisClientOptions { readonly env?: NodeJS.ProcessEnv; readonly redisConstructor?: RedisClientConstructor; readonly redisOptions?: RedisOptions; } export function resolveRedisUrl(env: NodeJS.ProcessEnv = process.env): string { const resolvedUrl = env.VALKEY_URL ?? env.REDIS_URL; if (typeof resolvedUrl !== 'string' || resolvedUrl.trim().length === 0) { throw new Error(ERR_MISSING_REDIS_URL); } return resolvedUrl; } export function createRedisClient( options: CreateRedisClientOptions = {}, ): TClient { const redisUrl = resolveRedisUrl(options.env); const RedisCtor = options.redisConstructor ?? (Redis as unknown as RedisClientConstructor); return new RedisCtor(redisUrl, { maxRetriesPerRequest: null, ...options.redisOptions, }); } export async function runRedisHealthCheck( client: RedisPingClient, ): Promise { const startedAt = process.hrtime.bigint(); try { const response = await client.ping(); const elapsedMs = Number((process.hrtime.bigint() - startedAt) / 1_000_000n); return { checkedAt: Date.now(), latencyMs: elapsedMs, ok: true, response, }; } catch (error) { const elapsedMs = Number((process.hrtime.bigint() - startedAt) / 1_000_000n); const message = error instanceof Error ? error.message : 'Unknown redis health check error'; return { checkedAt: Date.now(), latencyMs: elapsedMs, ok: false, error: message, }; } } export async function assertRedisHealthy( client: RedisPingClient, ): Promise { const health = await runRedisHealthCheck(client); if (!health.ok) { throw new Error( `Redis health check failed after ${health.latencyMs}ms: ${health.error ?? 'unknown error'}`, ); } return health; }