redis-cluster-client 0.0.4 → 0.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|