@@ -160,6 +160,7 @@ func (opt *RingOptions) clientOptions() *Options {
160160type ringShard struct {
161161 Client * Client
162162 down int32
163+ addr string
163164}
164165
165166func newRingShard (opt * RingOptions , name , addr string ) * ringShard {
@@ -168,6 +169,7 @@ func newRingShard(opt *RingOptions, name, addr string) *ringShard {
168169
169170 return & ringShard {
170171 Client : opt .NewClient (name , clopt ),
172+ addr : addr ,
171173 }
172174}
173175
@@ -212,33 +214,68 @@ type ringShards struct {
212214 opt * RingOptions
213215
214216 mu sync.RWMutex
217+ muClose sync.Mutex
215218 hash ConsistentHash
216- shards map [string ]* ringShard // read only
217- list []* ringShard // read only
219+ shards map [string ]* ringShard // read only, updated by SetAddrs
220+ list []* ringShard // read only, updated by SetAddrs
218221 numShard int
219222 closed bool
220223}
221224
222225func newRingShards (opt * RingOptions ) * ringShards {
223- shards := make (map [string ]* ringShard , len (opt .Addrs ))
224- list := make ([]* ringShard , 0 , len (shards ))
226+ c := & ringShards {
227+ opt : opt ,
228+ }
229+ c .SetAddrs (opt .Addrs )
225230
226- for name , addr := range opt .Addrs {
227- shard := newRingShard (opt , name , addr )
228- shards [name ] = shard
231+ return c
232+ }
229233
230- list = append (list , shard )
234+ // SetAddrs replaces the shards in use, such that you can increase and
235+ // decrease number of shards, that you use. It will reuse shards that
236+ // existed before and close the ones that will not be used anymore.
237+ func (c * ringShards ) SetAddrs (addrs map [string ]string ) {
238+ c .muClose .Lock ()
239+ defer c .muClose .Unlock ()
240+ if c .closed {
241+ return
231242 }
232243
233- c := & ringShards {
234- opt : opt ,
244+ shards := make (map [string ]* ringShard )
245+ unusedShards := make (map [string ]* ringShard )
246+
247+ for k , shard := range c .shards {
248+ if addr , ok := addrs [k ]; ok && shard .addr == addr {
249+ shards [k ] = shard
250+ } else {
251+ unusedShards [k ] = shard
252+ }
253+ }
235254
236- shards : shards ,
237- list : list ,
255+ for k , addr := range addrs {
256+ if shard , ok := c .shards [k ]; ! ok || shard .addr != addr {
257+ shards [k ] = newRingShard (c .opt , k , addr )
258+ }
238259 }
239- c .rebalance ()
240260
241- return c
261+ list := make ([]* ringShard , 0 , len (shards ))
262+ for _ , shard := range shards {
263+ list = append (list , shard )
264+ }
265+
266+ c .mu .Lock ()
267+ c .shards = shards
268+ c .list = list
269+
270+ c .rebalanceLocked ()
271+ c .mu .Unlock ()
272+
273+ for k , shard := range unusedShards {
274+ err := shard .Client .Close ()
275+ if err != nil {
276+ internal .Logger .Printf (context .Background (), "Failed to close ring shard client %s %s: %v" , k , shard .addr , err )
277+ }
278+ }
242279}
243280
244281func (c * ringShards ) List () []* ringShard {
@@ -355,6 +392,23 @@ func (c *ringShards) rebalance() {
355392 c .mu .Unlock ()
356393}
357394
395+ // rebalanceLocked removes dead shards from the Ring and callers need to hold the locl
396+ func (c * ringShards ) rebalanceLocked () {
397+ shards := c .shards
398+ liveShards := make ([]string , 0 , len (shards ))
399+
400+ for name , shard := range shards {
401+ if shard .IsUp () {
402+ liveShards = append (liveShards , name )
403+ }
404+ }
405+
406+ hash := c .opt .NewConsistentHash (liveShards )
407+
408+ c .hash = hash
409+ c .numShard = len (liveShards )
410+ }
411+
358412func (c * ringShards ) Len () int {
359413 c .mu .RLock ()
360414 defer c .mu .RUnlock ()
@@ -363,6 +417,8 @@ func (c *ringShards) Len() int {
363417}
364418
365419func (c * ringShards ) Close () error {
420+ c .muClose .Lock ()
421+ defer c .muClose .Unlock ()
366422 c .mu .Lock ()
367423 defer c .mu .Unlock ()
368424
@@ -436,6 +492,10 @@ func NewRing(opt *RingOptions) *Ring {
436492 return & ring
437493}
438494
495+ func (c * Ring ) SetAddrs (ctx context.Context , addrs map [string ]string ) {
496+ c .shards .SetAddrs (addrs )
497+ }
498+
439499// Do creates a Cmd from the args and processes the cmd.
440500func (c * Ring ) Do (ctx context.Context , args ... interface {}) * Cmd {
441501 cmd := NewCmd (ctx , args ... )
0 commit comments