@@ -18,6 +18,8 @@ import (
18
18
var ErrNoSlot = errors .New ("the slot has no redis node" )
19
19
var ErrReplicaOnlyConflict = errors .New ("ReplicaOnly conflicts with SendToReplicas option" )
20
20
var ErrInvalidShardsRefreshInterval = errors .New ("ShardsRefreshInterval must be greater than or equal to 0" )
21
+ var ErrReplicaOnlyConflictWithReplicaSelector = errors .New ("ReplicaOnly conflicts with ReplicaSelector option" )
22
+ var ErrSendToReplicasNotSet = errors .New ("SendToReplicas must be set when ReplicaSelector is set" )
21
23
22
24
type clusterClient struct {
23
25
pslots [16384 ]conn
@@ -42,6 +44,10 @@ type connrole struct {
42
44
//replica bool <- this field is removed because a server may have mixed roles at the same time in the future. https://github.com/valkey-io/valkey/issues/1372
43
45
}
44
46
47
+ var replicaOnlySelector = func (_ uint16 , replicas []ReplicaInfo ) int {
48
+ return util .FastRand (len (replicas ))
49
+ }
50
+
45
51
func newClusterClient (opt * ClientOption , connFn connFn , retryer retryHandler ) (* clusterClient , error ) {
46
52
client := & clusterClient {
47
53
cmd : cmds .NewBuilder (cmds .InitSlot ),
@@ -56,6 +62,16 @@ func newClusterClient(opt *ClientOption, connFn connFn, retryer retryHandler) (*
56
62
if opt .ReplicaOnly && opt .SendToReplicas != nil {
57
63
return nil , ErrReplicaOnlyConflict
58
64
}
65
+ if opt .ReplicaOnly && opt .ReplicaSelector != nil {
66
+ return nil , ErrReplicaOnlyConflictWithReplicaSelector
67
+ }
68
+ if opt .ReplicaSelector != nil && opt .SendToReplicas == nil {
69
+ return nil , ErrSendToReplicasNotSet
70
+ }
71
+
72
+ if opt .SendToReplicas != nil && opt .ReplicaSelector == nil {
73
+ opt .ReplicaSelector = replicaOnlySelector
74
+ }
59
75
60
76
if opt .SendToReplicas != nil {
61
77
rOpt := * opt
@@ -194,12 +210,12 @@ func (c *clusterClient) _refresh() (err error) {
194
210
for master , g := range groups {
195
211
conns [master ] = connrole {conn : c .connFn (master , c .opt )}
196
212
if c .rOpt != nil {
197
- for _ , addr := range g .nodes [1 :] {
198
- conns [addr ] = connrole {conn : c .connFn (addr , c .rOpt )}
213
+ for _ , nodeInfo := range g .nodes [1 :] {
214
+ conns [nodeInfo . Addr ] = connrole {conn : c .connFn (nodeInfo . Addr , c .rOpt )}
199
215
}
200
216
} else {
201
- for _ , addr := range g .nodes [1 :] {
202
- conns [addr ] = connrole {conn : c .connFn (addr , c .opt )}
217
+ for _ , nodeInfo := range g .nodes [1 :] {
218
+ conns [nodeInfo . Addr ] = connrole {conn : c .connFn (nodeInfo . Addr , c .opt )}
203
219
}
204
220
}
205
221
}
@@ -234,18 +250,25 @@ func (c *clusterClient) _refresh() (err error) {
234
250
nodesCount := len (g .nodes )
235
251
for _ , slot := range g .slots {
236
252
for i := slot [0 ]; i <= slot [1 ]; i ++ {
237
- pslots [i ] = conns [g .nodes [1 + util .FastRand (nodesCount - 1 )]].conn
253
+ pslots [i ] = conns [g .nodes [1 + util .FastRand (nodesCount - 1 )]. Addr ].conn
238
254
}
239
255
}
240
- case c .rOpt != nil : // implies c.opt.SendToReplicas != nil
256
+ case c .rOpt != nil :
241
257
if len (rslots ) == 0 { // lazy init
242
258
rslots = make ([]conn , 16384 )
243
259
}
244
260
if len (g .nodes ) > 1 {
261
+ n := len (g .nodes ) - 1
245
262
for _ , slot := range g .slots {
246
263
for i := slot [0 ]; i <= slot [1 ]; i ++ {
247
264
pslots [i ] = conns [master ].conn
248
- rslots [i ] = conns [g .nodes [1 + util .FastRand (len (g .nodes )- 1 )]].conn
265
+
266
+ rIndex := c .opt .ReplicaSelector (uint16 (i ), g .nodes [1 :])
267
+ if rIndex >= 0 && rIndex < n {
268
+ rslots [i ] = conns [g .nodes [1 + rIndex ].Addr ].conn
269
+ } else {
270
+ rslots [i ] = conns [master ].conn
271
+ }
249
272
}
250
273
}
251
274
} else {
@@ -297,8 +320,10 @@ func (c *clusterClient) nodes() []string {
297
320
return nodes
298
321
}
299
322
323
+ type nodes []ReplicaInfo
324
+
300
325
type group struct {
301
- nodes [] string
326
+ nodes nodes
302
327
slots [][2 ]int64
303
328
}
304
329
@@ -324,10 +349,10 @@ func parseSlots(slots RedisMessage, defaultAddr string) map[string]group {
324
349
g , ok := groups [master ]
325
350
if ! ok {
326
351
g .slots = make ([][2 ]int64 , 0 )
327
- g .nodes = make ([] string , 0 , len (v .values )- 2 )
352
+ g .nodes = make (nodes , 0 , len (v .values )- 2 )
328
353
for i := 2 ; i < len (v .values ); i ++ {
329
354
if dst := parseEndpoint (defaultAddr , v .values [i ].values [0 ].string , v .values [i ].values [1 ].integer ); dst != "" {
330
- g .nodes = append (g .nodes , dst )
355
+ g .nodes = append (g .nodes , ReplicaInfo { Addr : dst } )
331
356
}
332
357
}
333
358
}
@@ -345,16 +370,16 @@ func parseShards(shards RedisMessage, defaultAddr string, tls bool) map[string]g
345
370
m := - 1
346
371
shard , _ := v .AsMap ()
347
372
slots := shard ["slots" ].values
348
- nodes := shard ["nodes" ].values
373
+ _nodes := shard ["nodes" ].values
349
374
g := group {
350
- nodes : make ([] string , 0 , len (nodes )),
375
+ nodes : make (nodes , 0 , len (_nodes )),
351
376
slots : make ([][2 ]int64 , len (slots )/ 2 ),
352
377
}
353
378
for i := range g .slots {
354
379
g .slots [i ][0 ], _ = slots [i * 2 ].AsInt64 ()
355
380
g .slots [i ][1 ], _ = slots [i * 2 + 1 ].AsInt64 ()
356
381
}
357
- for _ , n := range nodes {
382
+ for _ , n := range _nodes {
358
383
dict , _ := n .AsMap ()
359
384
if dict ["health" ].string != "online" {
360
385
continue
@@ -367,12 +392,12 @@ func parseShards(shards RedisMessage, defaultAddr string, tls bool) map[string]g
367
392
if dict ["role" ].string == "master" {
368
393
m = len (g .nodes )
369
394
}
370
- g .nodes = append (g .nodes , dst )
395
+ g .nodes = append (g .nodes , ReplicaInfo { Addr : dst } )
371
396
}
372
397
}
373
398
if m >= 0 {
374
399
g .nodes [0 ], g .nodes [m ] = g .nodes [m ], g .nodes [0 ]
375
- groups [g .nodes [0 ]] = g
400
+ groups [g .nodes [0 ]. Addr ] = g
376
401
}
377
402
}
378
403
return groups
@@ -1078,15 +1103,15 @@ func (c *clusterClient) Dedicate() (DedicatedClient, func()) {
1078
1103
1079
1104
func (c * clusterClient ) Nodes () map [string ]Client {
1080
1105
c .mu .RLock ()
1081
- nodes := make (map [string ]Client , len (c .conns ))
1106
+ _nodes := make (map [string ]Client , len (c .conns ))
1082
1107
disableCache := c .opt != nil && c .opt .DisableCache
1083
1108
for addr , cc := range c .conns {
1084
1109
if ! cc .hidden {
1085
- nodes [addr ] = newSingleClientWithConn (cc .conn , c .cmd , c .retry , disableCache , c .retryHandler )
1110
+ _nodes [addr ] = newSingleClientWithConn (cc .conn , c .cmd , c .retry , disableCache , c .retryHandler )
1086
1111
}
1087
1112
}
1088
1113
c .mu .RUnlock ()
1089
- return nodes
1114
+ return _nodes
1090
1115
}
1091
1116
1092
1117
func (c * clusterClient ) Close () {
0 commit comments