1
0
mirror of https://github.com/redis/go-redis.git synced 2025-07-28 06:42:00 +03:00

fix: fixes ring.SetAddrs and rebalance race (#2283)

* fix: fixes ring.SetAddrs and rebalance race

The change ensures atomic update of `c.hash` and `c.shards`.

`BenchmarkRingRebalanceLocked` shows rebalance latency:
```
go test . -run=NONE -bench=BenchmarkRingRebalanceLocked -v -count=10 | benchstat /dev/stdin
name                   time/op
RingRebalanceLocked-8  8.50µs ±14%
```

(Note: it essentially reverts a46b053aa6)
This commit is contained in:
Alexander Yastrebov
2022-11-21 10:00:00 +01:00
committed by GitHub
parent bbff4dd5dc
commit d83436b321
3 changed files with 110 additions and 24 deletions

34
ring.go
View File

@ -254,9 +254,9 @@ func (c *ringSharding) SetAddrs(addrs map[string]string) {
shards, cleanup := c.newRingShards(addrs, c.shards)
c.shards = shards
c.rebalanceLocked()
c.mu.Unlock()
c.rebalance()
cleanup()
}
@ -388,7 +388,9 @@ func (c *ringSharding) Heartbeat(ctx context.Context, frequency time.Duration) {
}
if rebalance {
c.rebalance()
c.mu.Lock()
c.rebalanceLocked()
c.mu.Unlock()
}
case <-ctx.Done():
return
@ -396,32 +398,26 @@ func (c *ringSharding) Heartbeat(ctx context.Context, frequency time.Duration) {
}
}
// rebalance removes dead shards from the Ring.
func (c *ringSharding) rebalance() {
c.mu.RLock()
shards := c.shards
c.mu.RUnlock()
if shards == nil {
// rebalanceLocked removes dead shards from the Ring.
// Requires c.mu locked.
func (c *ringSharding) rebalanceLocked() {
if c.closed {
return
}
if c.shards == nil {
return
}
liveShards := make([]string, 0, len(shards.m))
liveShards := make([]string, 0, len(c.shards.m))
for name, shard := range shards.m {
for name, shard := range c.shards.m {
if shard.IsUp() {
liveShards = append(liveShards, name)
}
}
hash := c.opt.NewConsistentHash(liveShards)
c.mu.Lock()
if !c.closed {
c.hash = hash
c.numShard = len(liveShards)
}
c.mu.Unlock()
c.hash = c.opt.NewConsistentHash(liveShards)
c.numShard = len(liveShards)
}
func (c *ringSharding) Len() int {