fix(sentinel): keep jitter and failing timeout (#4196)

FailoverOptions.clusterOptions() did not copy ConnMaxLifetimeJitter,
so NewFailoverClusterClient ignored it. UniversalOptions.Failover()
also dropped FailingTimeoutSeconds, so NewUniversalClient with
MasterName plus RouteByLatency, RouteRandomly or IsClusterMode
ignored both options.
This commit is contained in:
nk-batman21 authored and GitHub committed 2026-10-26 09:46:12 +02:00
1 parent 454251f54b
commit 809ce09e4e
3 files changed
+44 -9

No files matched your search

+10 -9
View File
@@ -396,15 +396,16 @@ func (opt *FailoverOptions) clusterOptions() *ClusterOptions {
ContextTimeoutEnabled: opt.ContextTimeoutEnabled,
PoolFIFO: opt.PoolFIFO,
PoolSize: opt.PoolSize,
MaxConcurrentDials: opt.MaxConcurrentDials,
PoolTimeout: opt.PoolTimeout,
MinIdleConns: opt.MinIdleConns,
MaxIdleConns: opt.MaxIdleConns,
MaxActiveConns: opt.MaxActiveConns,
ConnMaxIdleTime: opt.ConnMaxIdleTime,
ConnMaxLifetime: opt.ConnMaxLifetime,
PoolFIFO: opt.PoolFIFO,
PoolSize: opt.PoolSize,
MaxConcurrentDials: opt.MaxConcurrentDials,
PoolTimeout: opt.PoolTimeout,
MinIdleConns: opt.MinIdleConns,
MaxIdleConns: opt.MaxIdleConns,
MaxActiveConns: opt.MaxActiveConns,
ConnMaxIdleTime: opt.ConnMaxIdleTime,
ConnMaxLifetime: opt.ConnMaxLifetime,
ConnMaxLifetimeJitter: opt.ConnMaxLifetimeJitter,
TLSConfig: opt.TLSConfig,
+33
View File
@@ -736,6 +736,39 @@ func compareSlices(t *testing.T, a, b []string, name string) {
}
}
func TestFailoverClusterClientKeepsOptions(t *testing.T) {
// The clients are only built, never used, so nothing needs to listen here.
addrs := []string{"127.0.0.1:1"}
clients := map[string]*redis.ClusterClient{
"NewFailoverClusterClient": redis.NewFailoverClusterClient(&redis.FailoverOptions{
MasterName: "mymaster",
SentinelAddrs: addrs,
RouteByLatency: true,
ConnMaxLifetime: time.Hour,
ConnMaxLifetimeJitter: 6 * time.Minute,
FailingTimeoutSeconds: 30,
}),
"NewUniversalClient": redis.NewUniversalClient(&redis.UniversalOptions{
MasterName: "mymaster",
Addrs: addrs,
RouteByLatency: true,
ConnMaxLifetime: time.Hour,
ConnMaxLifetimeJitter: 6 * time.Minute,
FailingTimeoutSeconds: 30,
}).(*redis.ClusterClient),
}
for name, client := range clients {
opt := client.Options()
if opt.ConnMaxLifetimeJitter != 6*time.Minute {
t.Errorf("%s: ConnMaxLifetimeJitter = %v, want 6m0s", name, opt.ConnMaxLifetimeJitter)
}
if opt.FailingTimeoutSeconds != 30 {
t.Errorf("%s: FailingTimeoutSeconds = %d, want 30", name, opt.FailingTimeoutSeconds)
}
_ = client.Close()
}
}
// countUsableReplicas mirrors the filter applied by parseReplicaAddrs in
// sentinel.go: a replica is usable only when it is not s_down, o_down, or
// disconnected. Plain len() on Sentinel's Replicas reply is not enough —
+1
View File
@@ -384,6 +384,7 @@ func (o *UniversalOptions) Failover() *FailoverOptions {
DisableIndentity: o.DisableIndentity,
IdentitySuffix: o.IdentitySuffix,
AutoPipelineOptions: o.AutoPipelineOptions,
FailingTimeoutSeconds: o.FailingTimeoutSeconds,
UnstableResp3: o.UnstableResp3,
PushNotificationProcessor: o.PushNotificationProcessor,
CommandMetadata: o.CommandMetadata,