redis-cluster-client 0.0.4 → 0.0.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/redis_client/cluster/errors.rb +1 -1
- data/lib/redis_client/cluster/node.rb +21 -15
- data/lib/redis_client/cluster.rb +44 -19
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: aff938c0517542c32f98206c28f1f605a52751e2e2e9a1bd10f5576285f568ee
|
4
|
+
data.tar.gz: 8a3d45fba93334959b1ee67504d2703bb116a8133ef8dac2aa8f314fdb74b268
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 63dec0155a50a0dbb7a17763c43291d371abe4e3df407340ba8edf125cf2c2bb338f607026a1d004dca489761cb2e6b5450e68a468f18799e5c3443b52a21be7
|
7
|
+
data.tar.gz: 4de6c4688ca120d7f4b636bd884195b3cddc3bf8d217ea662a1f382ced46de2c9e24b0e967a801d175f0572a083b9e5ea835d54797422cf60ce799cda3002f74
|
@@ -118,7 +118,7 @@ class RedisClient
|
|
118
118
|
try_map { |_, client| client.send(method, *command, **kwargs, &block) }.values
|
119
119
|
end
|
120
120
|
|
121
|
-
def
|
121
|
+
def call_primaries(method, *command, **kwargs, &block)
|
122
122
|
try_map do |node_key, client|
|
123
123
|
next if replica?(node_key)
|
124
124
|
|
@@ -126,22 +126,20 @@ class RedisClient
|
|
126
126
|
end.values
|
127
127
|
end
|
128
128
|
|
129
|
-
def
|
130
|
-
return
|
129
|
+
def call_replicas(method, *command, **kwargs, &block)
|
130
|
+
return call_primaries(method, *command, **kwargs, &block) if replica_disabled?
|
131
131
|
|
132
|
+
replica_node_keys = @replications.values.map(&:sample)
|
132
133
|
try_map do |node_key, client|
|
133
|
-
next if primary?(node_key)
|
134
|
+
next if primary?(node_key) || !replica_node_keys.include?(node_key)
|
134
135
|
|
135
136
|
client.send(method, *command, **kwargs, &block)
|
136
137
|
end.values
|
137
138
|
end
|
138
139
|
|
139
140
|
def scale_reading_clients
|
140
|
-
|
141
|
-
|
142
|
-
end
|
143
|
-
|
144
|
-
clients.values.sort_by do |client|
|
141
|
+
keys = replica_disabled? ? @replications.keys : @replications.values.map(&:first)
|
142
|
+
@clients.select { |k, _| keys.include?(k) }.values.sort_by do |client|
|
145
143
|
::RedisClient::Cluster::NodeKey.build_from_host_port(client.config.host, client.config.port)
|
146
144
|
end
|
147
145
|
end
|
@@ -173,6 +171,12 @@ class RedisClient
|
|
173
171
|
@mutex.synchronize { @slots[slot] = node_key }
|
174
172
|
end
|
175
173
|
|
174
|
+
def replicated?(primary_node_key, replica_node_key)
|
175
|
+
return false if @replications.nil? || @replications.size.zero?
|
176
|
+
|
177
|
+
@replications.fetch(primary_node_key).include?(replica_node_key)
|
178
|
+
end
|
179
|
+
|
176
180
|
private
|
177
181
|
|
178
182
|
def replica_disabled?
|
@@ -184,7 +188,9 @@ class RedisClient
|
|
184
188
|
end
|
185
189
|
|
186
190
|
def replica?(node_key)
|
187
|
-
|
191
|
+
return false if @replications.nil? || @replications.size.zero?
|
192
|
+
|
193
|
+
!@replications.key?(node_key)
|
188
194
|
end
|
189
195
|
|
190
196
|
def build_slot_node_mappings(node_info)
|
@@ -198,12 +204,12 @@ class RedisClient
|
|
198
204
|
slots
|
199
205
|
end
|
200
206
|
|
201
|
-
def build_replication_mappings(node_info)
|
207
|
+
def build_replication_mappings(node_info) # rubocop:disable Metrics/AbcSize
|
202
208
|
dict = node_info.to_h { |info| [info[:id], info] }
|
203
209
|
node_info.each_with_object(Hash.new { |h, k| h[k] = [] }) do |info, acc|
|
204
210
|
primary_info = dict[info[:primary_id]]
|
205
211
|
acc[primary_info[:node_key]] << info[:node_key] unless primary_info.nil?
|
206
|
-
acc[info[:node_key]]
|
212
|
+
acc[info[:node_key]] if info[:role] == 'master' # for the primary which have no replicas
|
207
213
|
end
|
208
214
|
end
|
209
215
|
|
@@ -222,14 +228,14 @@ class RedisClient
|
|
222
228
|
end
|
223
229
|
|
224
230
|
def try_map # rubocop:disable Metrics/MethodLength
|
225
|
-
errors = {}
|
226
231
|
results = {}
|
232
|
+
errors = {}
|
227
233
|
threads = @clients.map do |k, v|
|
228
234
|
Thread.new(k, v) do |node_key, client|
|
229
235
|
Thread.pass
|
230
236
|
reply = yield(node_key, client)
|
231
237
|
results[node_key] = reply unless reply.nil?
|
232
|
-
rescue
|
238
|
+
rescue StandardError => e
|
233
239
|
errors[node_key] = e
|
234
240
|
end
|
235
241
|
end
|
@@ -237,7 +243,7 @@ class RedisClient
|
|
237
243
|
threads.each(&:join)
|
238
244
|
return results if errors.empty?
|
239
245
|
|
240
|
-
raise ::RedisClient::Cluster::
|
246
|
+
raise ::RedisClient::Cluster::ErrorCollection, errors
|
241
247
|
end
|
242
248
|
end
|
243
249
|
end
|
data/lib/redis_client/cluster.rb
CHANGED
@@ -40,13 +40,13 @@ class RedisClient
|
|
40
40
|
@size.zero?
|
41
41
|
end
|
42
42
|
|
43
|
+
# TODO: https://github.com/redis-rb/redis-cluster-client/issues/37 handle redirections
|
43
44
|
def execute # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
|
44
|
-
all_replies =
|
45
|
+
all_replies = Array.new(@size)
|
46
|
+
errors = {}
|
45
47
|
threads = @grouped.map do |k, v|
|
46
48
|
Thread.new(@client, k, v) do |client, node_key, rows|
|
47
49
|
Thread.pass
|
48
|
-
|
49
|
-
node_key = node_key.nil? ? client.instance_variable_get(:@node).primary_node_keys.sample : node_key
|
50
50
|
replies = client.send(:find_node, node_key).pipelined do |pipeline|
|
51
51
|
rows.each do |row|
|
52
52
|
case row[1]
|
@@ -61,11 +61,15 @@ class RedisClient
|
|
61
61
|
raise ReplySizeError, "commands: #{rows.size}, replies: #{replies.size}" if rows.size != replies.size
|
62
62
|
|
63
63
|
rows.each_with_index { |row, idx| all_replies[row.first] = replies[idx] }
|
64
|
+
rescue StandardError => e
|
65
|
+
errors[node_key] = e
|
64
66
|
end
|
65
67
|
end
|
66
68
|
|
67
69
|
threads.each(&:join)
|
68
|
-
all_replies
|
70
|
+
return all_replies if errors.empty?
|
71
|
+
|
72
|
+
raise ::RedisClient::Cluster::ErrorCollection, errors
|
69
73
|
end
|
70
74
|
end
|
71
75
|
|
@@ -183,13 +187,13 @@ class RedisClient
|
|
183
187
|
def send_command(method, *command, **kwargs, &block) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength
|
184
188
|
cmd = command.first.to_s.downcase
|
185
189
|
case cmd
|
186
|
-
when 'acl', 'auth', 'bgrewriteaof', 'bgsave', 'quit', 'save'
|
190
|
+
when 'acl', 'auth', 'bgrewriteaof', 'bgsave', 'quit', 'save', 'ping'
|
187
191
|
@node.call_all(method, *command, **kwargs, &block).first
|
188
192
|
when 'flushall', 'flushdb'
|
189
|
-
@node.
|
190
|
-
when 'wait' then
|
191
|
-
when 'keys' then @node.
|
192
|
-
when 'dbsize' then @node.
|
193
|
+
@node.call_primaries(method, *command, **kwargs, &block).first
|
194
|
+
when 'wait' then send_wait_command(method, *command, **kwargs, &block)
|
195
|
+
when 'keys' then @node.call_replicas(method, *command, **kwargs, &block).flatten.sort
|
196
|
+
when 'dbsize' then @node.call_replicas(method, *command, **kwargs, &block).sum
|
193
197
|
when 'scan' then _scan(*command, **kwargs)
|
194
198
|
when 'lastsave' then @node.call_all(method, *command, **kwargs, &block).sort
|
195
199
|
when 'role' then @node.call_all(method, *command, **kwargs, &block)
|
@@ -207,6 +211,19 @@ class RedisClient
|
|
207
211
|
node = assign_node(*command)
|
208
212
|
try_send(node, method, *command, **kwargs, &block)
|
209
213
|
end
|
214
|
+
rescue RedisClient::Cluster::ErrorCollection => e
|
215
|
+
update_cluster_info! if e.errors.values.map(&:class).any?(::RedisClient::ConnectionError)
|
216
|
+
raise
|
217
|
+
end
|
218
|
+
|
219
|
+
def send_wait_command(method, *command, retry_count: 3, **kwargs, &block)
|
220
|
+
@node.call_primaries(method, *command, **kwargs, &block).sum
|
221
|
+
rescue RedisClient::Cluster::ErrorCollection => e
|
222
|
+
raise if retry_count <= 0 || e.errors.values.map(&:message).grep(/ERR WAIT cannot be used with replica instances/).empty?
|
223
|
+
|
224
|
+
update_cluster_info!
|
225
|
+
retry_count -= 1
|
226
|
+
retry
|
210
227
|
end
|
211
228
|
|
212
229
|
def send_config_command(method, *command, **kwargs, &block)
|
@@ -234,13 +251,17 @@ class RedisClient
|
|
234
251
|
end
|
235
252
|
end
|
236
253
|
|
237
|
-
def send_cluster_command(method, *command, **kwargs, &block)
|
254
|
+
def send_cluster_command(method, *command, **kwargs, &block) # rubocop:disable Metrics/MethodLength
|
238
255
|
subcommand = command[1].to_s.downcase
|
239
256
|
case subcommand
|
240
257
|
when 'addslots', 'delslots', 'failover', 'forget', 'meet', 'replicate',
|
241
258
|
'reset', 'set-config-epoch', 'setslot'
|
242
259
|
raise ::RedisClient::Cluster::OrchestrationCommandNotSupported, ['cluster', subcommand]
|
243
260
|
when 'saveconfig' then @node.call_all(method, *command, **kwargs, &block).first
|
261
|
+
when 'getkeysinslot'
|
262
|
+
raise ArgumentError, command.join(' ') if command.size != 4
|
263
|
+
|
264
|
+
find_node(@node.find_node_key_of_replica(command[2])).send(method, *command, **kwargs, &block)
|
244
265
|
else assign_node(*command).send(method, *command, **kwargs, &block)
|
245
266
|
end
|
246
267
|
end
|
@@ -250,7 +271,7 @@ class RedisClient
|
|
250
271
|
when 'debug', 'kill'
|
251
272
|
@node.call_all(method, *command, **kwargs, &block).first
|
252
273
|
when 'flush', 'load'
|
253
|
-
@node.
|
274
|
+
@node.call_primaries(method, *command, **kwargs, &block).first
|
254
275
|
else assign_node(*command).send(method, *command, **kwargs, &block)
|
255
276
|
end
|
256
277
|
end
|
@@ -268,18 +289,16 @@ class RedisClient
|
|
268
289
|
|
269
290
|
# @see https://redis.io/topics/cluster-spec#redirection-and-resharding
|
270
291
|
# Redirection and resharding
|
271
|
-
def try_send(node, method, *args, retry_count: 3, **kwargs, &block) # rubocop:disable Metrics/
|
292
|
+
def try_send(node, method, *args, retry_count: 3, **kwargs, &block) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
272
293
|
node.send(method, *args, **kwargs, &block)
|
273
294
|
rescue ::RedisClient::CommandError => e
|
274
|
-
if
|
275
|
-
raise if retry_count <= 0
|
295
|
+
raise if retry_count <= 0
|
276
296
|
|
297
|
+
if e.message.start_with?(REPLY_MOVED)
|
277
298
|
node = assign_redirection_node(e.message)
|
278
299
|
retry_count -= 1
|
279
300
|
retry
|
280
301
|
elsif e.message.start_with?(REPLY_ASK)
|
281
|
-
raise if retry_count <= 0
|
282
|
-
|
283
302
|
node = assign_asking_node(e.message)
|
284
303
|
node.call(CMD_ASKING)
|
285
304
|
retry_count -= 1
|
@@ -288,8 +307,11 @@ class RedisClient
|
|
288
307
|
raise
|
289
308
|
end
|
290
309
|
rescue ::RedisClient::ConnectionError
|
310
|
+
raise if retry_count <= 0
|
311
|
+
|
291
312
|
update_cluster_info!
|
292
|
-
|
313
|
+
retry_count -= 1
|
314
|
+
retry
|
293
315
|
end
|
294
316
|
|
295
317
|
def _scan(*command, **kwargs) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
@@ -349,13 +371,16 @@ class RedisClient
|
|
349
371
|
end
|
350
372
|
end
|
351
373
|
|
352
|
-
def find_node(node_key)
|
374
|
+
def find_node(node_key, retry_count: 3)
|
353
375
|
return @node.sample if node_key.nil?
|
354
376
|
|
355
377
|
@node.find_by(node_key)
|
356
378
|
rescue ::RedisClient::Cluster::Node::ReloadNeeded
|
379
|
+
raise(::RedisClient::ConnectionError, 'unstable cluster state') if retry_count <= 0
|
380
|
+
|
357
381
|
update_cluster_info!(node_key)
|
358
|
-
|
382
|
+
retry_count -= 1
|
383
|
+
retry
|
359
384
|
end
|
360
385
|
|
361
386
|
def update_cluster_info!(node_key = nil)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: redis-cluster-client
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Taishi Kasuga
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-06-
|
11
|
+
date: 2022-06-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: redis-client
|