1
1
package redis
2
2
3
3
import (
4
- "errors"
5
- "io"
6
4
"math/rand"
7
- "net"
8
5
"strings"
9
6
"sync"
10
7
"sync/atomic"
11
8
"time"
12
9
)
13
10
11
+ func removeDuplicates (slice []string ) []string {
12
+ seen := make (map [string ]struct {}, len (slice ))
13
+ for i := 0 ; i < len (slice ); {
14
+ addr := slice [i ]
15
+ if _ , ok := seen [addr ]; ok {
16
+ slice = append (slice [:i ], slice [i + 1 :]... )
17
+ } else {
18
+ seen [addr ] = struct {}{}
19
+ i ++
20
+ }
21
+ }
22
+ return slice
23
+ }
24
+
14
25
type ClusterClient struct {
15
26
commandable
16
27
17
- addrs map [ string ] struct {}
28
+ addrs [] string
18
29
slots [][]string
19
30
slotsMx sync.RWMutex // protects slots & addrs cache
20
31
21
- conns map [string ]* Client
22
- connsMx sync.Mutex // protects conns
32
+ clients map [string ]* Client
33
+ clientsMx sync.RWMutex // protects clients
23
34
24
35
opt * ClusterOptions
25
36
@@ -28,95 +39,122 @@ type ClusterClient struct {
28
39
29
40
// NewClusterClient initializes a new cluster-aware client using given options.
30
41
// A list of seed addresses must be provided.
31
- func NewClusterClient (opt * ClusterOptions ) (* ClusterClient , error ) {
32
- addrs , err := opt .getAddrSet ()
33
- if err != nil {
34
- return nil , err
35
- }
36
-
42
+ func NewClusterClient (opt * ClusterOptions ) * ClusterClient {
37
43
client := & ClusterClient {
38
- addrs : addrs ,
39
- conns : make (map [string ]* Client ),
44
+ addrs : opt . getAddrs () ,
45
+ clients : make (map [string ]* Client ),
40
46
opt : opt ,
41
47
_reload : 1 ,
42
48
}
43
49
client .commandable .process = client .process
44
- client .reloadIfDue ()
45
50
go client .reaper (time .NewTicker (5 * time .Minute ))
46
- return client , nil
51
+ return client
47
52
}
48
53
49
- // Close closes the cluster connection
54
+ // Close closes the cluster client.
50
55
func (c * ClusterClient ) Close () error {
51
- c .slotsMx .Lock ()
52
- defer c .slotsMx .Unlock ()
53
-
54
- return c .reset ()
56
+ // TODO: close should make client unusable
57
+ c .setSlots (nil )
58
+ return nil
55
59
}
56
60
57
61
// ------------------------------------------------------------------------
58
62
59
- // Finds the current master address for a given hash slot
60
- func (c * ClusterClient ) getMasterAddrBySlot (hashSlot int ) string {
61
- if addrs := c .slots [hashSlot ]; len (addrs ) > 0 {
62
- return addrs [0 ]
63
+ // getClient returns a Client for a given address.
64
+ func (c * ClusterClient ) getClient (addr string ) * Client {
65
+ c .clientsMx .RLock ()
66
+ client , ok := c .clients [addr ]
67
+ if ok {
68
+ c .clientsMx .RUnlock ()
69
+ return client
63
70
}
64
- return ""
65
- }
71
+ c .clientsMx .RUnlock ()
66
72
67
- // Returns a node's client for a given address
68
- func (c * ClusterClient ) getNodeClientByAddr (addr string ) * Client {
69
- c .connsMx .Lock ()
70
- client , ok := c .conns [addr ]
73
+ c .clientsMx .Lock ()
74
+ client , ok = c .clients [addr ]
71
75
if ! ok {
72
76
opt := c .opt .clientOptions ()
73
77
opt .Addr = addr
74
78
client = NewTCPClient (opt )
75
- c .conns [addr ] = client
79
+ c .clients [addr ] = client
76
80
}
77
- c .connsMx .Unlock ()
81
+ c .clientsMx .Unlock ()
82
+
78
83
return client
79
84
}
80
85
86
+ // randomClient returns a Client for the first live node.
87
+ func (c * ClusterClient ) randomClient () (client * Client , err error ) {
88
+ for i := 0 ; i < 10 ; i ++ {
89
+ n := rand .Intn (len (c .addrs ))
90
+ client = c .getClient (c .addrs [n ])
91
+ err = client .Ping ().Err ()
92
+ if err == nil {
93
+ return client , nil
94
+ }
95
+ }
96
+ return nil , err
97
+ }
98
+
81
99
// Process a command
82
100
func (c * ClusterClient ) process (cmd Cmder ) {
101
+ var client * Client
83
102
var ask bool
84
103
85
104
c .reloadIfDue ()
86
105
87
- hashSlot := hashSlot (cmd .clusterKey ())
106
+ slot := hashSlot (cmd .clusterKey ())
88
107
89
108
c .slotsMx .RLock ()
90
109
defer c .slotsMx .RUnlock ()
91
110
92
- tried := make (map [string ]struct {}, len (c .addrs ))
93
- addr := c .getMasterAddrBySlot (hashSlot )
94
- for attempt := 0 ; attempt <= c .opt .getMaxRedirects (); attempt ++ {
95
- tried [addr ] = struct {}{}
111
+ addrs := c .slots [slot ]
112
+ if len (addrs ) > 0 {
113
+ // First address is master.
114
+ client = c .getClient (addrs [0 ])
115
+ } else {
116
+ var err error
117
+ client , err = c .randomClient ()
118
+ if err != nil {
119
+ cmd .setErr (err )
120
+ return
121
+ }
122
+ }
123
+
124
+ // Index in the addrs slice pointing to the next replica.
125
+ replicaIndex := 1
96
126
97
- // Pick the connection, process request
98
- conn := c .getNodeClientByAddr (addr )
127
+ for attempt := 0 ; attempt <= c .opt .getMaxRedirects (); attempt ++ {
99
128
if ask {
100
- pipe := conn .Pipeline ()
129
+ pipe := client .Pipeline ()
101
130
pipe .Process (NewCmd ("ASKING" ))
102
131
pipe .Process (cmd )
103
132
_ , _ = pipe .Exec ()
104
133
ask = false
105
134
} else {
106
- conn .Process (cmd )
135
+ client .Process (cmd )
107
136
}
108
137
109
138
// If there is no (real) error, we are done!
110
139
err := cmd .Err ()
111
- if err == nil || err == Nil {
140
+ if err == nil || err == Nil || err == TxFailedErr {
112
141
return
113
142
}
114
143
115
- // On connection errors, pick a random, previosuly untried connection
116
- // and request again.
117
- if _ , ok := err .(* net.OpError ); ok || err == io .EOF {
118
- if addr = c .findNextAddr (tried ); addr == "" {
119
- return
144
+ // On network errors try another node.
145
+ if isNetworkError (err ) {
146
+ if replicaIndex < len (addrs ) {
147
+ // Try next available replica.
148
+ client = c .getClient (addrs [replicaIndex ])
149
+ replicaIndex ++
150
+ cmd .reset ()
151
+ continue
152
+ } else {
153
+ // Otherwise try random node.
154
+ client , err = c .randomClient ()
155
+ if err != nil {
156
+ return
157
+ }
120
158
}
121
159
cmd .reset ()
122
160
continue
@@ -131,94 +169,70 @@ func (c *ClusterClient) process(cmd Cmder) {
131
169
// Handle MOVE and ASK redirections, return on any other error
132
170
switch parts [0 ] {
133
171
case "MOVED" :
134
- c .forceReload ()
135
- addr = parts [2 ]
172
+ c .scheduleReload ()
173
+ client = c . getClient ( parts [2 ])
136
174
case "ASK" :
137
175
ask = true
138
- addr = parts [2 ]
176
+ client = c . getClient ( parts [2 ])
139
177
default :
140
178
return
141
179
}
142
180
cmd .reset ()
143
181
}
144
182
}
145
183
146
- // Closes all connections and reloads slot cache, if due
147
- func (c * ClusterClient ) reloadIfDue () (err error ) {
148
- if ! atomic .CompareAndSwapUint32 (& c ._reload , 1 , 0 ) {
149
- return
184
+ // Closes all clients and returns last error if there are any.
185
+ func (c * ClusterClient ) resetClients () (err error ) {
186
+ c .clientsMx .Lock ()
187
+ for addr , client := range c .clients {
188
+ if e := client .Close (); e != nil {
189
+ err = e
190
+ }
191
+ delete (c .clients , addr )
150
192
}
193
+ c .clientsMx .Unlock ()
194
+ return err
195
+ }
151
196
152
- var infos []ClusterSlotInfo
153
-
197
+ func (c * ClusterClient ) setSlots (slots []ClusterSlotInfo ) {
154
198
c .slotsMx .Lock ()
155
- defer c .slotsMx .Unlock ()
156
-
157
- // Try known addresses in random order (map interation order is random in Go)
158
- // http://redis.io/topics/cluster-spec#clients-first-connection-and-handling-of-redirections
159
- // https://github.com/antirez/redis-rb-cluster/blob/fd931ed/cluster.rb#L157
160
- for addr := range c .addrs {
161
- c .reset ()
162
199
163
- infos , err = c . fetchClusterSlots ( addr )
164
- if err == nil {
165
- c . update ( infos )
166
- break
200
+ c . slots = make ([][] string , hashSlots )
201
+ for _ , info := range slots {
202
+ for i := info . Start ; i <= info . End ; i ++ {
203
+ c . slots [ i ] = info . Addrs
167
204
}
205
+ c .addrs = append (c .addrs , info .Addrs ... )
168
206
}
169
- return
170
- }
207
+ c . addrs = removeDuplicates ( c . addrs )
208
+ c . resetClients ()
171
209
172
- // Closes all connections and flushes slots cache
173
- func (c * ClusterClient ) reset () (err error ) {
174
- c .connsMx .Lock ()
175
- for addr , client := range c .conns {
176
- if e := client .Close (); e != nil {
177
- err = e
178
- }
179
- delete (c .conns , addr )
180
- }
181
- c .connsMx .Unlock ()
182
- c .slots = make ([][]string , hashSlots )
183
- return
210
+ c .slotsMx .Unlock ()
184
211
}
185
212
186
- // Forces a cache reload on next request
187
- func (c * ClusterClient ) forceReload () {
188
- atomic .StoreUint32 (& c ._reload , 1 )
189
- }
213
+ // Closes all connections and reloads slot cache, if due.
214
+ func (c * ClusterClient ) reloadIfDue () (err error ) {
215
+ if ! atomic .CompareAndSwapUint32 (& c ._reload , 1 , 0 ) {
216
+ return
217
+ }
190
218
191
- // Find the next untried address
192
- func (c * ClusterClient ) findNextAddr (tried map [string ]struct {}) string {
193
- for addr := range c .addrs {
194
- if _ , ok := tried [addr ]; ! ok {
195
- return addr
196
- }
219
+ client , err := c .randomClient ()
220
+ if err != nil {
221
+ return err
197
222
}
198
- return ""
199
- }
200
223
201
- // Fetch slot information
202
- func (c * ClusterClient ) fetchClusterSlots (addr string ) ([]ClusterSlotInfo , error ) {
203
- opt := c .opt .clientOptions ()
204
- opt .Addr = addr
205
- client := NewClient (opt )
206
- defer client .Close ()
224
+ slots , err := client .ClusterSlots ().Result ()
225
+ if err != nil {
226
+ return err
227
+ }
228
+ c .setSlots (slots )
207
229
208
- return client . ClusterSlots (). Result ()
230
+ return nil
209
231
}
210
232
211
- // Update slot information, populate slots
212
- func (c * ClusterClient ) update (infos []ClusterSlotInfo ) {
213
- for _ , info := range infos {
214
- for i := info .Start ; i <= info .End ; i ++ {
215
- c .slots [i ] = info .Addrs
216
- }
217
-
218
- for _ , addr := range info .Addrs {
219
- c .addrs [addr ] = struct {}{}
220
- }
221
- }
233
+ // Schedules slots reload on next request.
234
+ func (c * ClusterClient ) scheduleReload () {
235
+ atomic .StoreUint32 (& c ._reload , 1 )
222
236
}
223
237
224
238
// reaper closes idle connections to the cluster.
@@ -237,8 +251,6 @@ func (c *ClusterClient) reaper(ticker *time.Ticker) {
237
251
238
252
//------------------------------------------------------------------------------
239
253
240
- var errNoAddrs = errors .New ("redis: no addresses" )
241
-
242
254
type ClusterOptions struct {
243
255
// A seed-list of host:port addresses of known cluster nodes
244
256
Addrs []string
@@ -278,17 +290,9 @@ func (opt *ClusterOptions) getMaxRedirects() int {
278
290
return opt .MaxRedirects
279
291
}
280
292
281
- func (opt * ClusterOptions ) getAddrSet () (map [string ]struct {}, error ) {
282
- size := len (opt .Addrs )
283
- if size < 1 {
284
- return nil , errNoAddrs
285
- }
286
-
287
- addrs := make (map [string ]struct {}, size )
288
- for _ , addr := range opt .Addrs {
289
- addrs [addr ] = struct {}{}
290
- }
291
- return addrs , nil
293
+ func (opt * ClusterOptions ) getAddrs () []string {
294
+ opt .Addrs = removeDuplicates (opt .Addrs )
295
+ return opt .Addrs
292
296
}
293
297
294
298
func (opt * ClusterOptions ) clientOptions () * Options {
0 commit comments