@@ -163,10 +163,12 @@ var (
163163
164164// --- Configuration ---
165165var (
166- RedisPassword = getEnv ("REDIS_PASSWORD" , "" )
167- RedisMasterName = getEnv ("REDIS_MASTER_NAME" , "mymaster" )
168- RedisSentinelAddr = getEnv ("REDIS_SENTINEL_ADDR" , "localhost:26379" )
169- RedisSentinelPass = getEnv ("REDIS_SENTINEL_PASSWORD" , "" )
166+ RedisPassword = getEnv ("REDIS_PASSWORD" , "" )
167+ RedisMasterName = getEnv ("REDIS_MASTER_NAME" , "mymaster" )
168+ RedisSentinelAddr = getEnv ("REDIS_SENTINEL_ADDR" , "localhost:26379" )
169+ RedisSentinelPass = getEnv ("REDIS_SENTINEL_PASSWORD" , "" )
170+ RedisConnectRetries = getEnvInt ("REDIS_CONNECT_RETRIES" , 30 ) // Max retries for initial connection
171+ RedisRetryInterval = getEnvInt ("REDIS_RETRY_INTERVAL_SEC" , 2 ) // Seconds between retries
170172 Port = getEnv ("PORT" , "8080" )
171173 AdminAPIKey = getEnv ("ADMIN_API_KEY" , "" )
172174 Concurrency = getEnvInt ("WORKER_CONCURRENCY" , 10 )
@@ -638,6 +640,44 @@ func initTracer(ctx context.Context) (func(context.Context) error, error) {
638640 return tp .Shutdown , nil
639641}
640642
643+ // waitForRedis attempts to connect to Redis with retries
644+ // This handles the case where Redis Sentinel may not be ready when the app starts
645+ func waitForRedis (ctx context.Context , client * redis.Client ) error {
646+ retryInterval := time .Duration (RedisRetryInterval ) * time .Second
647+
648+ for attempt := 1 ; attempt <= RedisConnectRetries ; attempt ++ {
649+ err := client .Ping (ctx ).Err ()
650+ if err == nil {
651+ if attempt > 1 {
652+ slog .Info ("Redis connection established" , "attempt" , attempt )
653+ }
654+ return nil
655+ }
656+
657+ if attempt == RedisConnectRetries {
658+ return fmt .Errorf ("failed after %d attempts: %w" , attempt , err )
659+ }
660+
661+ slog .Warn ("Redis not ready, retrying..." ,
662+ "attempt" , attempt ,
663+ "max_attempts" , RedisConnectRetries ,
664+ "retry_in_seconds" , RedisRetryInterval ,
665+ "master" , RedisMasterName ,
666+ "sentinel" , RedisSentinelAddr ,
667+ "error" , err ,
668+ )
669+
670+ select {
671+ case <- ctx .Done ():
672+ return ctx .Err ()
673+ case <- time .After (retryInterval ):
674+ // Continue to next attempt
675+ }
676+ }
677+
678+ return fmt .Errorf ("max retries exceeded" )
679+ }
680+
641681func main () {
642682 initLogger ()
643683
@@ -682,9 +722,9 @@ func main() {
682722 })
683723 defer redisClient .Close ()
684724
685- // Verify Redis connection
686- if err := redisClient . Ping (ctx ). Err ( ); err != nil {
687- slog .Error ("Failed to connect to Redis via Sentinel" , "master" , RedisMasterName , "sentinel" , RedisSentinelAddr , "error" , err )
725+ // Verify Redis connection with retries
726+ if err := waitForRedis (ctx , redisClient ); err != nil {
727+ slog .Error ("Failed to connect to Redis via Sentinel after retries " , "master" , RedisMasterName , "sentinel" , RedisSentinelAddr , "error" , err )
688728 os .Exit (1 )
689729 }
690730 slog .Info ("Connected to Redis via Sentinel" , "master" , RedisMasterName , "sentinel" , RedisSentinelAddr )
0 commit comments