@@ -13,17 +13,19 @@ import (
1313
1414const masterOnlyFlag = 0x4000
1515
16- func (c * Cluster ) slotRangesAndInternalMasterOnly () ([]redisclusterutil.SlotsRange , error ) {
16+ func (c * Cluster ) slotRangesAndInternalMasterOnly () ([]redisclusterutil.SlotsRange , map [ string ] struct {}, error ) {
1717 nodes := c .getConfig ().nodes
1818
1919 var ranges []redisclusterutil.SlotsRange
20+ var failedMasters map [string ]struct {}
2021 var err error
2122Outter:
2223 for _ , node := range nodes {
2324 for _ , conn := range node .conns {
2425 resp := redis.Sync {conn }.Do ("CLUSTER SLOTS" )
2526 ranges , err = redisclusterutil .ParseSlotsInfo (resp )
2627 if err == nil {
28+ failedMasters = failedMastersOf (redis.Sync {conn }.Do ("CLUSTER NODES" ))
2729 break Outter
2830 }
2931 c .report (LogClusterSlotsError {Conn : conn , Error : err })
@@ -32,7 +34,7 @@ Outter:
3234 }
3335 if err != nil {
3436 c .report (LogSlotRangeError {})
35- return nil , c .err (ErrClusterSlots )
37+ return nil , nil , c .err (ErrClusterSlots )
3638 }
3739
3840 // look for reminder about future migrations
@@ -43,10 +45,28 @@ Outter:
4345 }
4446 c .m .Unlock ()
4547
46- return ranges , nil
48+ return ranges , failedMasters , nil
4749}
4850
49- func (c * Cluster ) updateMappings (slotRanges []redisclusterutil.SlotsRange ) {
51+ // failedMastersOf lists the masters the cluster agrees are down. A node's own
52+ // suspicion (fail?) does not count. Without a readable CLUSTER NODES nothing counts,
53+ // and the link-down tolerance alone decides.
54+ func failedMastersOf (res interface {}) map [string ]struct {} {
55+ infos , err := redisclusterutil .ParseClusterNodes (res )
56+ if err != nil {
57+ return nil
58+ }
59+ failed := map [string ]struct {}{}
60+ for i := range infos {
61+ ii := & infos [i ]
62+ if ii .IsMaster () && ii .Fail && ! ii .PFail && ii .HasAddr () {
63+ failed [ii .Addr ] = struct {}{}
64+ }
65+ }
66+ return failed
67+ }
68+
69+ func (c * Cluster ) updateMappings (slotRanges []redisclusterutil.SlotsRange , failedMasters map [string ]struct {}) {
5070 shards := make (map [string ][]string )
5171 for _ , r := range slotRanges {
5272 shards [r .Addrs [0 ]] = r .Addrs
@@ -122,19 +142,23 @@ func (c *Cluster) updateMappings(slotRanges []redisclusterutil.SlotsRange) {
122142 return sh
123143 }()
124144
125- if oldshard != nil {
126- newConfig .shards [shardno ] = oldshard
127- } else {
128- shard := & shard {
145+ sh := oldshard
146+ if sh == nil {
147+ sh = & shard {
129148 addr : addrs ,
130149 good : (uint32 (1 ) << uint (len (addrs ))) - 1 ,
131150 pingWeights : make ([]uint32 , len (addrs )),
132151 }
133- newConfig .shards [shardno ] = shard
134- for i := range shard .pingWeights {
135- shard .pingWeights [i ] = 1
152+ for i := range sh .pingWeights {
153+ sh .pingWeights [i ] = 1
136154 }
137155 }
156+ masterFailed := uint32 (0 )
157+ if _ , ok := failedMasters [master ]; ok {
158+ masterFailed = 1
159+ }
160+ atomic .StoreUint32 (& sh .masterFailed , masterFailed )
161+ newConfig .shards [shardno ] = sh
138162 newConfig .masters [addrs [0 ]] = shardno
139163 random = shardno
140164 }
@@ -230,7 +254,7 @@ func (s *shard) setReplicaInfo(res interface{}, n uint64, tolerance time.Duratio
230254 } else if buf , ok := res .([]byte ); ! ok {
231255 haserr = true
232256 } else {
233- haserr = ! replicaHealthy (buf , tolerance )
257+ haserr = ! replicaHealthy (buf , tolerance , atomic . LoadUint32 ( & s . masterFailed ) != 0 )
234258 }
235259 for {
236260 oldstate := atomic .LoadUint32 (& s .good )
@@ -252,7 +276,9 @@ func (s *shard) setReplicaInfo(res interface{}, n uint64, tolerance time.Duratio
252276// replicaHealthy tells whether INFO output describes a replica worth reading from.
253277// master_link_down_since_seconds is -1 for a replica that has never synced since it
254278// started, and its dataset is then anything from empty to the RDB it booted from.
255- func replicaHealthy (info []byte , tolerance time.Duration ) bool {
279+ // A replica cut off from a master the cluster has declared failed cannot fall further
280+ // behind: nobody accepts writes for the shard until a new master is elected.
281+ func replicaHealthy (info []byte , tolerance time.Duration , masterFailed bool ) bool {
256282 if bytes .Contains (info , []byte ("loading:1" )) {
257283 return false
258284 }
@@ -263,7 +289,7 @@ func replicaHealthy(info []byte, tolerance time.Duration) bool {
263289 if ! ok || since < 0 {
264290 return false
265291 }
266- return time .Duration (since )* time .Second < tolerance
292+ return masterFailed || time .Duration (since )* time .Second < tolerance
267293}
268294
269295func infoInt (info []byte , field string ) (int64 , bool ) {
0 commit comments