@@ -145,13 +145,11 @@ func main() {
145145
146146 // Shared Redis client used by both on-call and the agent worker. We open
147147 // it once here so both subsystems share connections.
148- var sharedRedis * redis.Client
148+ var sharedRedis redis.UniversalClient
149149
150150 if cfg .OnCall .Enable || cfg .OnCall .InitializedOnly {
151- redisOptions := handlerRedisOptions (cfg .Redis )
152-
153- // Initialize Redis client
154- redisClient := redis .NewClient (redisOptions )
151+ // Initialize Redis client (cluster-aware when redis.cluster is set)
152+ redisClient := newRedisClient (cfg .Redis )
155153
156154 // Test Redis connection
157155 if err := redisClient .Ping (context .Background ()).Err (); err != nil {
@@ -179,7 +177,7 @@ func main() {
179177 // in-memory cursors when Redis isn't reachable).
180178 rdb := sharedRedis
181179 if rdb == nil && cfg .Redis .Host != "" {
182- rdb = redis . NewClient ( handlerRedisOptions ( cfg .Redis ) )
180+ rdb = newRedisClient ( cfg .Redis )
183181 if err := rdb .Ping (context .Background ()).Err (); err != nil {
184182 log .Printf ("agent: Redis unavailable (%v); cursors will be in-memory only" , err )
185183 rdb = nil
@@ -225,7 +223,7 @@ func main() {
225223// startAgent constructs the worker, starts it in a goroutine, and registers
226224// admin routes on the fiber app. It returns the catalog so the caller can
227225// hold a reference (and so future hot-reload code has a handle to it).
228- func startAgent (ctx context.Context , app * fiber.App , cfg c.AgentConfig , gatewaySecret string , store storage.Provider , rdb * redis.Client ) (* agent.Catalog , error ) {
226+ func startAgent (ctx context.Context , app * fiber.App , cfg c.AgentConfig , gatewaySecret string , store storage.Provider , rdb redis.UniversalClient ) (* agent.Catalog , error ) {
229227 // On the Postgres backend, install the typed signal-table
230228 // catalog store so the log catalog reads/writes the explicit
231229 // vs_patterns/vs_logs/vs_services tables (searchable, indexed) instead of
@@ -408,6 +406,24 @@ func handleQueueMessage(content *map[string]interface{}) error {
408406 return services .CreateIncident ("" , content , & overwrite ) // teamID as empty string
409407}
410408
409+ // newRedisClient builds the Redis client both subsystems share. It reuses
410+ // handlerRedisOptions for the addr/password/TLS settings, then returns either a
411+ // single-node client (the default) or a cluster-aware client when the operator
412+ // opts in via redis.cluster / REDIS_CLUSTER. Both concrete types satisfy
413+ // redis.UniversalClient, so callers thread the interface and single-key
414+ // Get/Set follow MOVED/ASK redirects transparently in cluster mode.
415+ func newRedisClient (rc c.RedisConfig ) redis.UniversalClient {
416+ opts := handlerRedisOptions (rc )
417+ if rc .ClusterEnabled () {
418+ return redis .NewClusterClient (& redis.ClusterOptions {
419+ Addrs : []string {opts .Addr },
420+ Password : opts .Password ,
421+ TLSConfig : opts .TLSConfig ,
422+ })
423+ }
424+ return redis .NewClient (opts )
425+ }
426+
411427func handlerRedisOptions (rc c.RedisConfig ) * redis.Options {
412428 redisOptions := & redis.Options {
413429 Addr : rc .Host + ":" + strconv .Itoa (rc .Port ),
0 commit comments