Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions src/services/redis-status.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { Logger } from "@/util/logger";

class RedisConnectionStatus {
private static instance: RedisConnectionStatus;
private isConnected: boolean = false;
private logger = new Logger('RedisConnectionStatus');

private constructor() {}

static getInstance(): RedisConnectionStatus {
if (!RedisConnectionStatus.instance) {
RedisConnectionStatus.instance = new RedisConnectionStatus();
}
return RedisConnectionStatus.instance;
}

setConnected(status: boolean) {
this.isConnected = status;
}

isRedisConnected(): boolean {
return this.isConnected;
}
}

export const redisStatus = RedisConnectionStatus.getInstance();
64 changes: 50 additions & 14 deletions src/services/redis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,60 +29,96 @@ function redisClientSingleton(config = getRedisConfiguration()):
db: config.db,
maxRetriesPerRequest: 3,
lazyConnect: true,
connectTimeout: 3000, // Add explicit connection timeout
commandTimeout: 3000, // Add command timeout
reconnectOnError: (err) => {
const targetError = 'READONLY';
if (err.message.includes(targetError)) {
// Only reconnect on specific errors
return true;
}
return false;
},
retryStrategy: (times) => {
const delay = Math.min(times * 50, 2000); // exponentially increase the retry delay, but not more than 2 seconds

if (times > 5) {
// If we've retried at least 5 times, give up.
if (times > 3) { // Reduce max retries from 5 to 3
return null;
}

return delay;
},
return Math.min(times * 100, 1000); // More conservative retry delay
}
};

if (config.isSentinelEnabled) {
// If sentinel is enabled, use the sentinel configuration
logger.log(`Setting Redis options for sentinel configuration`);
options = {
...options,
sentinels: config.sentinels,
sentinelPassword: config.sentinelPassword,
password: config.password,
name: config.name,
enableReadyCheck: false, // Disable ready check for faster connection
};
} else {
// If sentinel is not enabled, use the standalone Redis configuration
logger.log(`Setting Redis options for single instance`);
options = {
...options,
host: config.host,
port: config.port,
password: config.password,
enableReadyCheck: false, // Disable ready check for faster connection
};
}

const redis = new Redis(options);

// Add connection event handlers
redis.on("connect", () => {
logger.verbose("[Redis] Connected successfully");
});

redis.on("error", (error: unknown) => {
logger.error(`[Redis] Error connecting: ${error}`);
});

// Add connection ready handler
redis.on("ready", () => {
logger.verbose("[Redis] Client is ready");
});

// Handle cases where Redis becomes unavailable after initial connection
redis.on("close", () => {
logger.warn("[Redis] Connection closed");
});

return {
isEnabled: true,
client: redis,
};
} catch (e) {
logger.error(`[Redis] Could not create a Redis instance`);
throw new Error(`[Redis] Could not create a Redis instance`);
logger.error(`[Redis] Could not create a Redis instance: ${e}`);
// Return disabled state instead of throwing
return {
isEnabled: false,
};
}
}

// Typed access to the global context
// Modify the global singleton initialization to be more resilient
const globalForRedis = globalThis as unknown as {
redis: ReturnType<typeof redisClientSingleton> | undefined;
};

const redis = globalForRedis.redis ?? redisClientSingleton();
globalForRedis.redis = redis;
// Add initialization retry with timeout
const initializeRedis = () => {
try {
const redis = redisClientSingleton();
globalForRedis.redis = redis;
return redis;
} catch (error) {
console.error("[Redis] Failed to initialize Redis client:", error);
return { isEnabled: false };
}
};

const redis = globalForRedis.redis ?? initializeRedis();

export default redis;