From 2cd0483ba1f5c87cd9ff620b7fa4a68fd9495768 Mon Sep 17 00:00:00 2001 From: Sergey Zagursky Date: Mon, 14 Sep 2026 19:18:48 +0100 Subject: [PATCH] Fall back to any reachable host of a shard for replica reads A shard's health mask is a hard filter in connForPolicySlaves, and its bits contradict each other while a master is down: the master's bit is never cleared, while its replica reports master_link_status:down for the whole failover window even though it keeps serving reads. Every MasterAndSlaves / PreferSlaves read to that shard fails with no_alive_connection until redis finishes the failover and CLUSTER SLOTS reports a new master. Health becomes a preference: hosts in the mask are tried first, the rest of the shard after them. Zero-weight hosts are tried last rather than never, since the weighted wheel cannot draw from a total of zero. --- rediscluster/mapping.go | 67 ++++++++++++++++----------- rediscluster/mapping_internal_test.go | 32 +++++++++++++ 2 files changed, 73 insertions(+), 26 deletions(-) create mode 100644 rediscluster/mapping_internal_test.go diff --git a/rediscluster/mapping.go b/rediscluster/mapping.go index 36d5cf5..c696985 100644 --- a/rediscluster/mapping.go +++ b/rediscluster/mapping.go @@ -3,6 +3,7 @@ package rediscluster import ( "crypto/tls" "fmt" + "math/bits" "sync/atomic" "unsafe" @@ -278,33 +279,39 @@ func (c *Cluster) connForPolicySlaves(policy ReplicaPolicyEnum, seen []*rediscon weights := c.weightsForPolicySlaves(policy, shard) health := atomic.LoadUint32(&shard.good) // load health information - healthWeight := c.getHealthWeight(weights, health) + all := uint32(1)< 0 { + r = nextRng(off, total) + } for i, w := range weights { - if health&(1<