redis-cluster-client 0.13.0 → 0.13.1
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/command.rb +34 -33
- data/lib/redis_client/cluster/node.rb +48 -3
- data/lib/redis_client/cluster/pipeline.rb +2 -2
- data/lib/redis_client/cluster/router.rb +110 -89
- data/lib/redis_client/cluster.rb +4 -4
- data/lib/redis_client/cluster_config.rb +0 -1
- 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: 2bc3348020af7012ab193ef18128f2cd37028c9225f1e7fa9555d3609ff92362
|
4
|
+
data.tar.gz: 3c08890f1c50f297f0d60f0503bd268742bbcd242a32ac73df733496a013660e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8a4036ef8be3c29c747abc7deb59698fbf5d5e581ab9913fd3e06d530a9bae7eba13a397929bb9e3eaf611d5ee01960378ffef54bc6f69706aaf672bc9f38f89
|
7
|
+
data.tar.gz: '08897fd0c13bb175399b9118783e812f20555e0e09d021cf24fab236168d8ffd5a3ad4599f2ba1db56ccebaf501f9319c605940069c963f54b2268e1acad5dae'
|
@@ -46,14 +46,28 @@ class RedisClient
|
|
46
46
|
|
47
47
|
private
|
48
48
|
|
49
|
-
def parse_command_reply(rows)
|
49
|
+
def parse_command_reply(rows) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity
|
50
50
|
rows&.each_with_object({}) do |row, acc|
|
51
|
-
next if row
|
51
|
+
next if row.first.nil?
|
52
|
+
|
53
|
+
# TODO: in redis 7.0 or later, subcommand information included in the command reply
|
54
|
+
|
55
|
+
pos = case row.first
|
56
|
+
when 'eval', 'evalsha', 'zinterstore', 'zunionstore' then 3
|
57
|
+
when 'object', 'xgroup' then 2
|
58
|
+
when 'migrate', 'xread', 'xreadgroup' then 0
|
59
|
+
else row[3]
|
60
|
+
end
|
61
|
+
|
62
|
+
writable = case row.first
|
63
|
+
when 'xgroup' then true
|
64
|
+
else row[2].include?('write')
|
65
|
+
end
|
52
66
|
|
53
67
|
acc[row.first] = ::RedisClient::Cluster::Command::Detail.new(
|
54
|
-
first_key_position:
|
68
|
+
first_key_position: pos,
|
55
69
|
key_step: row[5],
|
56
|
-
write?:
|
70
|
+
write?: writable,
|
57
71
|
readonly?: row[2].include?('readonly')
|
58
72
|
)
|
59
73
|
end.freeze || EMPTY_HASH
|
@@ -90,42 +104,29 @@ class RedisClient
|
|
90
104
|
end
|
91
105
|
|
92
106
|
def determine_first_key_position(command) # rubocop:disable Metrics/CyclomaticComplexity, Metrics/AbcSize, Metrics/PerceivedComplexity
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
find_command_info(command.first)&.first_key_position.to_i
|
99
|
-
elsif command.first.casecmp('mset').zero?
|
100
|
-
find_command_info(command.first)&.first_key_position.to_i
|
101
|
-
elsif command.first.casecmp('del').zero?
|
102
|
-
find_command_info(command.first)&.first_key_position.to_i
|
103
|
-
elsif command.first.casecmp('eval').zero?
|
104
|
-
3
|
105
|
-
elsif command.first.casecmp('evalsha').zero?
|
106
|
-
3
|
107
|
-
elsif command.first.casecmp('zinterstore').zero?
|
108
|
-
3
|
109
|
-
elsif command.first.casecmp('zunionstore').zero?
|
110
|
-
3
|
111
|
-
elsif command.first.casecmp('object').zero?
|
112
|
-
2
|
113
|
-
elsif command.first.casecmp('memory').zero?
|
114
|
-
command[1].to_s.casecmp('usage').zero? ? 2 : 0
|
115
|
-
elsif command.first.casecmp('migrate').zero?
|
116
|
-
command[3].empty? ? determine_optional_key_position(command, 'keys') : 3
|
117
|
-
elsif command.first.casecmp('xread').zero?
|
107
|
+
i = find_command_info(command.first)&.first_key_position.to_i
|
108
|
+
return i if i > 0
|
109
|
+
|
110
|
+
cmd_name = command.first
|
111
|
+
if cmd_name.casecmp('xread').zero?
|
118
112
|
determine_optional_key_position(command, 'streams')
|
119
|
-
elsif
|
113
|
+
elsif cmd_name.casecmp('xreadgroup').zero?
|
120
114
|
determine_optional_key_position(command, 'streams')
|
115
|
+
elsif cmd_name.casecmp('migrate').zero?
|
116
|
+
command[3].empty? ? determine_optional_key_position(command, 'keys') : 3
|
117
|
+
elsif cmd_name.casecmp('memory').zero?
|
118
|
+
command[1].to_s.casecmp('usage').zero? ? 2 : 0
|
121
119
|
else
|
122
|
-
|
120
|
+
i
|
123
121
|
end
|
124
122
|
end
|
125
123
|
|
126
124
|
def determine_optional_key_position(command, option_name)
|
127
|
-
|
128
|
-
|
125
|
+
command.each_with_index do |e, i|
|
126
|
+
return i + 1 if e.to_s.downcase(:ascii) == option_name
|
127
|
+
end
|
128
|
+
|
129
|
+
0
|
129
130
|
end
|
130
131
|
end
|
131
132
|
end
|
@@ -7,6 +7,7 @@ require 'redis_client/cluster/node/primary_only'
|
|
7
7
|
require 'redis_client/cluster/node/random_replica'
|
8
8
|
require 'redis_client/cluster/node/random_replica_or_primary'
|
9
9
|
require 'redis_client/cluster/node/latency_replica'
|
10
|
+
require 'redis_client/cluster/node_key'
|
10
11
|
|
11
12
|
class RedisClient
|
12
13
|
class Cluster
|
@@ -43,6 +44,10 @@ class RedisClient
|
|
43
44
|
def replica?
|
44
45
|
role == 'slave'
|
45
46
|
end
|
47
|
+
|
48
|
+
def serialize(str)
|
49
|
+
str << id << node_key << role << primary_id << config_epoch
|
50
|
+
end
|
46
51
|
end
|
47
52
|
|
48
53
|
class CharArray
|
@@ -338,9 +343,7 @@ class RedisClient
|
|
338
343
|
|
339
344
|
grouped = node_info_list.compact.group_by do |info_list|
|
340
345
|
info_list.sort_by!(&:id)
|
341
|
-
info_list.each_with_object(String.new(capacity: 128 * info_list.size))
|
342
|
-
a << e.id << e.node_key << e.role << e.primary_id << e.config_epoch
|
343
|
-
end
|
346
|
+
info_list.each_with_object(String.new(capacity: 128 * info_list.size)) { |e, a| e.serialize(a) }
|
344
347
|
end
|
345
348
|
|
346
349
|
grouped.max_by { |_, v| v.size }[1].first
|
@@ -375,6 +378,48 @@ class RedisClient
|
|
375
378
|
end
|
376
379
|
end
|
377
380
|
|
381
|
+
def parse_cluster_slots_reply(reply) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
382
|
+
reply.group_by { |e| e[2][2] }.each_with_object([]) do |(primary_id, group), acc|
|
383
|
+
slots = group.map { |e| e[0, 2] }.freeze
|
384
|
+
|
385
|
+
group.first[2..].each do |arr|
|
386
|
+
ip = arr[0]
|
387
|
+
next if ip.nil? || ip.empty? || ip == '?'
|
388
|
+
|
389
|
+
id = arr[2]
|
390
|
+
role = id == primary_id ? 'master' : 'slave'
|
391
|
+
acc << ::RedisClient::Cluster::Node::Info.new(
|
392
|
+
id: id,
|
393
|
+
node_key: NodeKey.build_from_host_port(ip, arr[1]),
|
394
|
+
role: role,
|
395
|
+
primary_id: role == 'master' ? nil : primary_id,
|
396
|
+
slots: role == 'master' ? slots : EMPTY_ARRAY
|
397
|
+
)
|
398
|
+
end
|
399
|
+
end.freeze
|
400
|
+
end
|
401
|
+
|
402
|
+
def parse_cluster_shards_reply(reply) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
403
|
+
reply.each_with_object([]) do |shard, acc|
|
404
|
+
nodes = shard.fetch('nodes')
|
405
|
+
primary_id = nodes.find { |n| n.fetch('role') == 'master' }.fetch('id')
|
406
|
+
|
407
|
+
nodes.each do |node|
|
408
|
+
ip = node.fetch('ip')
|
409
|
+
next if node.fetch('health') != 'online' || ip.nil? || ip.empty? || ip == '?'
|
410
|
+
|
411
|
+
role = node.fetch('role')
|
412
|
+
acc << ::RedisClient::Cluster::Node::Info.new(
|
413
|
+
id: node.fetch('id'),
|
414
|
+
node_key: NodeKey.build_from_host_port(ip, node['port'] || node['tls-port']),
|
415
|
+
role: role == 'master' ? role : 'slave',
|
416
|
+
primary_id: role == 'master' ? nil : primary_id,
|
417
|
+
slots: role == 'master' ? shard.fetch('slots').each_slice(2).to_a.freeze : EMPTY_ARRAY
|
418
|
+
)
|
419
|
+
end
|
420
|
+
end.freeze
|
421
|
+
end
|
422
|
+
|
378
423
|
# As redirection node_key is dependent on `cluster-preferred-endpoint-type` config,
|
379
424
|
# node_key should use hostname if present in CLUSTER NODES output.
|
380
425
|
#
|
@@ -283,9 +283,9 @@ class RedisClient
|
|
283
283
|
args = timeout.nil? ? [] : [timeout]
|
284
284
|
|
285
285
|
if block.nil?
|
286
|
-
@router.
|
286
|
+
@router.send_command_to_node(node, method, command, args)
|
287
287
|
else
|
288
|
-
@router.
|
288
|
+
@router.send_command_to_node(node, method, command, args, &block)
|
289
289
|
end
|
290
290
|
end
|
291
291
|
|
@@ -22,6 +22,13 @@ class RedisClient
|
|
22
22
|
|
23
23
|
attr_reader :config
|
24
24
|
|
25
|
+
Action = Struct.new(
|
26
|
+
'RedisCommandRoutingAction',
|
27
|
+
:method_name,
|
28
|
+
:reply_transformer,
|
29
|
+
keyword_init: true
|
30
|
+
)
|
31
|
+
|
25
32
|
def initialize(config, concurrent_worker, pool: nil, **kwargs)
|
26
33
|
@config = config
|
27
34
|
@concurrent_worker = concurrent_worker
|
@@ -31,87 +38,20 @@ class RedisClient
|
|
31
38
|
@node.reload!
|
32
39
|
@command = ::RedisClient::Cluster::Command.load(@node.replica_clients.shuffle, slow_command_timeout: config.slow_command_timeout)
|
33
40
|
@command_builder = @config.command_builder
|
41
|
+
@dedicated_actions = build_dedicated_actions
|
34
42
|
rescue ::RedisClient::Cluster::InitialSetupError => e
|
35
43
|
e.with_config(config)
|
36
44
|
raise
|
37
45
|
end
|
38
46
|
|
39
|
-
def send_command(method, command, *args, &block) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
node = assign_node(command)
|
48
|
-
try_send(node, method, command, args, &block)
|
49
|
-
elsif cmd_name.casecmp('mset').zero?
|
50
|
-
send_multiple_keys_command(command.first, method, command, args, &block)
|
51
|
-
elsif cmd_name.casecmp('del').zero?
|
52
|
-
send_multiple_keys_command(command.first, method, command, args, &block)
|
53
|
-
elsif cmd_name.casecmp('ping').zero?
|
54
|
-
@node.send_ping(method, command, args).first.then(&TSF.call(block))
|
55
|
-
elsif cmd_name.casecmp('wait').zero?
|
56
|
-
send_wait_command(method, command, args, &block)
|
57
|
-
elsif cmd_name.casecmp('keys').zero?
|
58
|
-
@node.call_replicas(method, command, args).flatten.sort_by(&:to_s).then(&TSF.call(block))
|
59
|
-
elsif cmd_name.casecmp('dbsize').zero?
|
60
|
-
@node.call_replicas(method, command, args).select { |e| e.is_a?(Integer) }.sum.then(&TSF.call(block))
|
61
|
-
elsif cmd_name.casecmp('scan').zero?
|
62
|
-
scan(command, seed: 1)
|
63
|
-
elsif cmd_name.casecmp('lastsave').zero?
|
64
|
-
@node.call_all(method, command, args).sort_by(&:to_i).then(&TSF.call(block))
|
65
|
-
elsif cmd_name.casecmp('role').zero?
|
66
|
-
@node.call_all(method, command, args, &block)
|
67
|
-
elsif cmd_name.casecmp('config').zero?
|
68
|
-
send_config_command(method, command, args, &block)
|
69
|
-
elsif cmd_name.casecmp('client').zero?
|
70
|
-
send_client_command(method, command, args, &block)
|
71
|
-
elsif cmd_name.casecmp('cluster').zero?
|
72
|
-
send_cluster_command(method, command, args, &block)
|
73
|
-
elsif cmd_name.casecmp('memory').zero?
|
74
|
-
send_memory_command(method, command, args, &block)
|
75
|
-
elsif cmd_name.casecmp('script').zero?
|
76
|
-
send_script_command(method, command, args, &block)
|
77
|
-
elsif cmd_name.casecmp('pubsub').zero?
|
78
|
-
send_pubsub_command(method, command, args, &block)
|
79
|
-
elsif cmd_name.casecmp('watch').zero?
|
80
|
-
send_watch_command(command, &block)
|
81
|
-
elsif cmd_name.casecmp('acl').zero?
|
82
|
-
@node.call_all(method, command, args).first.then(&TSF.call(block))
|
83
|
-
elsif cmd_name.casecmp('auth').zero?
|
84
|
-
@node.call_all(method, command, args).first.then(&TSF.call(block))
|
85
|
-
elsif cmd_name.casecmp('bgrewriteaof').zero?
|
86
|
-
@node.call_all(method, command, args).first.then(&TSF.call(block))
|
87
|
-
elsif cmd_name.casecmp('bgsave').zero?
|
88
|
-
@node.call_all(method, command, args).first.then(&TSF.call(block))
|
89
|
-
elsif cmd_name.casecmp('quit').zero?
|
90
|
-
@node.call_all(method, command, args).first.then(&TSF.call(block))
|
91
|
-
elsif cmd_name.casecmp('save').zero?
|
92
|
-
@node.call_all(method, command, args).first.then(&TSF.call(block))
|
93
|
-
elsif cmd_name.casecmp('flushall').zero?
|
94
|
-
@node.call_primaries(method, command, args).first.then(&TSF.call(block))
|
95
|
-
elsif cmd_name.casecmp('flushdb').zero?
|
96
|
-
@node.call_primaries(method, command, args).first.then(&TSF.call(block))
|
97
|
-
elsif cmd_name.casecmp('readonly').zero?
|
98
|
-
raise ::RedisClient::Cluster::OrchestrationCommandNotSupported.from_command(command.first).with_config(@config)
|
99
|
-
elsif cmd_name.casecmp('readwrite').zero?
|
100
|
-
raise ::RedisClient::Cluster::OrchestrationCommandNotSupported.from_command(command.first).with_config(@config)
|
101
|
-
elsif cmd_name.casecmp('shutdown').zero?
|
102
|
-
raise ::RedisClient::Cluster::OrchestrationCommandNotSupported.from_command(command.first).with_config(@config)
|
103
|
-
elsif cmd_name.casecmp('discard').zero?
|
104
|
-
raise ::RedisClient::Cluster::AmbiguousNodeError.from_command(command.first).with_config(@config)
|
105
|
-
elsif cmd_name.casecmp('exec').zero?
|
106
|
-
raise ::RedisClient::Cluster::AmbiguousNodeError.from_command(command.first).with_config(@config)
|
107
|
-
elsif cmd_name.casecmp('multi').zero?
|
108
|
-
raise ::RedisClient::Cluster::AmbiguousNodeError.from_command(command.first).with_config(@config)
|
109
|
-
elsif cmd_name.casecmp('unwatch').zero?
|
110
|
-
raise ::RedisClient::Cluster::AmbiguousNodeError.from_command(command.first).with_config(@config)
|
111
|
-
else
|
112
|
-
node = assign_node(command)
|
113
|
-
try_send(node, method, command, args, &block)
|
114
|
-
end
|
47
|
+
def send_command(method, command, *args, &block) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
48
|
+
return assign_node_and_send_command(method, command, args, &block) unless @dedicated_actions.key?(command.first)
|
49
|
+
|
50
|
+
action = @dedicated_actions[command.first]
|
51
|
+
return send(action.method_name, method, command, args, &block) if action.reply_transformer.nil?
|
52
|
+
|
53
|
+
reply = send(action.method_name, method, command, args)
|
54
|
+
action.reply_transformer.call(reply).then(&TSF.call(block))
|
115
55
|
rescue ::RedisClient::CircuitBreaker::OpenCircuitError
|
116
56
|
raise
|
117
57
|
rescue ::RedisClient::Cluster::Node::ReloadNeeded
|
@@ -137,7 +77,12 @@ class RedisClient
|
|
137
77
|
end
|
138
78
|
|
139
79
|
# @see https://redis.io/docs/reference/cluster-spec/#redirection-and-resharding Redirection and resharding
|
140
|
-
def
|
80
|
+
def assign_node_and_send_command(method, command, args, retry_count: 3, &block)
|
81
|
+
node = assign_node(command)
|
82
|
+
send_command_to_node(node, method, command, args, retry_count: retry_count, &block)
|
83
|
+
end
|
84
|
+
|
85
|
+
def send_command_to_node(node, method, command, args, retry_count: 3, &block)
|
141
86
|
handle_redirection(node, command, retry_count: retry_count) do |on_node|
|
142
87
|
if args.empty?
|
143
88
|
# prevent memory allocation for variable-length args
|
@@ -312,6 +257,81 @@ class RedisClient
|
|
312
257
|
|
313
258
|
private
|
314
259
|
|
260
|
+
def build_dedicated_actions # rubocop:disable Metrics/AbcSize
|
261
|
+
pick_first = ->(reply) { reply.first } # rubocop:disable Style/SymbolProc
|
262
|
+
multiple_key_action = Action.new(method_name: :send_multiple_keys_command)
|
263
|
+
all_node_first_action = Action.new(method_name: :send_command_to_all_nodes, reply_transformer: pick_first)
|
264
|
+
primary_first_action = Action.new(method_name: :send_command_to_primaries, reply_transformer: pick_first)
|
265
|
+
not_supported_action = Action.new(method_name: :fail_not_supported_command)
|
266
|
+
keyless_action = Action.new(method_name: :fail_keyless_command)
|
267
|
+
actions = {
|
268
|
+
'ping' => Action.new(method_name: :send_ping_command, reply_transformer: pick_first),
|
269
|
+
'wait' => Action.new(method_name: :send_wait_command),
|
270
|
+
'keys' => Action.new(method_name: :send_command_to_replicas, reply_transformer: ->(reply) { reply.flatten.sort_by(&:to_s) }),
|
271
|
+
'dbsize' => Action.new(method_name: :send_command_to_replicas, reply_transformer: ->(reply) { reply.select { |e| e.is_a?(Integer) }.sum }),
|
272
|
+
'scan' => Action.new(method_name: :send_scan_command),
|
273
|
+
'lastsave' => Action.new(method_name: :send_command_to_all_nodes, reply_transformer: ->(reply) { reply.sort_by(&:to_i) }),
|
274
|
+
'role' => Action.new(method_name: :send_command_to_all_nodes),
|
275
|
+
'config' => Action.new(method_name: :send_config_command),
|
276
|
+
'client' => Action.new(method_name: :send_client_command),
|
277
|
+
'cluster' => Action.new(method_name: :send_cluster_command),
|
278
|
+
'memory' => Action.new(method_name: :send_memory_command),
|
279
|
+
'script' => Action.new(method_name: :send_script_command),
|
280
|
+
'pubsub' => Action.new(method_name: :send_pubsub_command),
|
281
|
+
'watch' => Action.new(method_name: :send_watch_command),
|
282
|
+
'mget' => multiple_key_action,
|
283
|
+
'mset' => multiple_key_action,
|
284
|
+
'del' => multiple_key_action,
|
285
|
+
'acl' => all_node_first_action,
|
286
|
+
'auth' => all_node_first_action,
|
287
|
+
'bgrewriteaof' => all_node_first_action,
|
288
|
+
'bgsave' => all_node_first_action,
|
289
|
+
'quit' => all_node_first_action,
|
290
|
+
'save' => all_node_first_action,
|
291
|
+
'flushall' => primary_first_action,
|
292
|
+
'flushdb' => primary_first_action,
|
293
|
+
'readonly' => not_supported_action,
|
294
|
+
'readwrite' => not_supported_action,
|
295
|
+
'shutdown' => not_supported_action,
|
296
|
+
'discard' => keyless_action,
|
297
|
+
'exec' => keyless_action,
|
298
|
+
'multi' => keyless_action,
|
299
|
+
'unwatch' => keyless_action
|
300
|
+
}.freeze
|
301
|
+
actions.each_with_object({}) do |(k, v), acc|
|
302
|
+
acc[k] = v
|
303
|
+
acc[k.upcase] = v
|
304
|
+
end.freeze
|
305
|
+
end
|
306
|
+
|
307
|
+
def send_command_to_all_nodes(method, command, args, &block)
|
308
|
+
@node.call_all(method, command, args, &block)
|
309
|
+
end
|
310
|
+
|
311
|
+
def send_command_to_primaries(method, command, args, &block)
|
312
|
+
@node.call_primaries(method, command, args, &block)
|
313
|
+
end
|
314
|
+
|
315
|
+
def send_command_to_replicas(method, command, args, &block)
|
316
|
+
@node.call_replicas(method, command, args, &block)
|
317
|
+
end
|
318
|
+
|
319
|
+
def send_ping_command(method, command, args, &block)
|
320
|
+
@node.send_ping(method, command, args, &block)
|
321
|
+
end
|
322
|
+
|
323
|
+
def send_scan_command(_method, command, _args, &_block)
|
324
|
+
scan(command, seed: 1)
|
325
|
+
end
|
326
|
+
|
327
|
+
def fail_not_supported_command(_method, command, _args, &_block)
|
328
|
+
raise ::RedisClient::Cluster::OrchestrationCommandNotSupported.from_command(command.first).with_config(@config)
|
329
|
+
end
|
330
|
+
|
331
|
+
def fail_keyless_command(_method, command, _args, &_block)
|
332
|
+
raise ::RedisClient::Cluster::AmbiguousNodeError.from_command(command.first).with_config(@config)
|
333
|
+
end
|
334
|
+
|
315
335
|
def send_wait_command(method, command, args, retry_count: 1, &block) # rubocop:disable Metrics/AbcSize
|
316
336
|
@node.call_primaries(method, command, args).select { |r| r.is_a?(Integer) }.sum.then(&TSF.call(block))
|
317
337
|
rescue ::RedisClient::Cluster::ErrorCollection => e
|
@@ -362,23 +382,23 @@ class RedisClient
|
|
362
382
|
|
363
383
|
def send_cluster_command(method, command, args, &block) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
364
384
|
if command[1].casecmp('addslots').zero?
|
365
|
-
|
385
|
+
fail_not_supported_command(method, command, args, &block)
|
366
386
|
elsif command[1].casecmp('delslots').zero?
|
367
|
-
|
387
|
+
fail_not_supported_command(method, command, args, &block)
|
368
388
|
elsif command[1].casecmp('failover').zero?
|
369
|
-
|
389
|
+
fail_not_supported_command(method, command, args, &block)
|
370
390
|
elsif command[1].casecmp('forget').zero?
|
371
|
-
|
391
|
+
fail_not_supported_command(method, command, args, &block)
|
372
392
|
elsif command[1].casecmp('meet').zero?
|
373
|
-
|
393
|
+
fail_not_supported_command(method, command, args, &block)
|
374
394
|
elsif command[1].casecmp('replicate').zero?
|
375
|
-
|
395
|
+
fail_not_supported_command(method, command, args, &block)
|
376
396
|
elsif command[1].casecmp('reset').zero?
|
377
|
-
|
397
|
+
fail_not_supported_command(method, command, args, &block)
|
378
398
|
elsif command[1].casecmp('set-config-epoch').zero?
|
379
|
-
|
399
|
+
fail_not_supported_command(method, command, args, &block)
|
380
400
|
elsif command[1].casecmp('setslot').zero?
|
381
|
-
|
401
|
+
fail_not_supported_command(method, command, args, &block)
|
382
402
|
elsif command[1].casecmp('saveconfig').zero?
|
383
403
|
@node.call_all(method, command, args).first.then(&TSF.call(block))
|
384
404
|
elsif command[1].casecmp('getkeysinslot').zero?
|
@@ -427,7 +447,7 @@ class RedisClient
|
|
427
447
|
end
|
428
448
|
end
|
429
449
|
|
430
|
-
def send_watch_command(command)
|
450
|
+
def send_watch_command(_method, command, _args, &_block)
|
431
451
|
unless block_given?
|
432
452
|
msg = 'A block required. And you need to use the block argument as a client for the transaction.'
|
433
453
|
raise ::RedisClient::Cluster::Transaction::ConsistencyError.new(msg).with_config(@config)
|
@@ -442,8 +462,9 @@ class RedisClient
|
|
442
462
|
end
|
443
463
|
end
|
444
464
|
|
445
|
-
def send_multiple_keys_command(
|
465
|
+
def send_multiple_keys_command(method, command, args, &block) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
446
466
|
# This implementation is prioritized performance rather than readability or so.
|
467
|
+
cmd = command.first
|
447
468
|
if cmd.casecmp('mget').zero?
|
448
469
|
single_key_cmd = 'get'
|
449
470
|
keys_step = 1
|
@@ -457,7 +478,7 @@ class RedisClient
|
|
457
478
|
raise NotImplementedError, cmd
|
458
479
|
end
|
459
480
|
|
460
|
-
return
|
481
|
+
return assign_node_and_send_command(method, command, args, &block) if command.size <= keys_step + 1 || ::RedisClient::Cluster::KeySlotConverter.hash_tag_included?(command[1])
|
461
482
|
|
462
483
|
seed = @config.use_replica? && @config.replica_affinity == :random ? nil : Random.new_seed
|
463
484
|
pipeline = ::RedisClient::Cluster::Pipeline.new(self, @command_builder, @concurrent_worker, exception: true, seed: seed)
|
data/lib/redis_client/cluster.rb
CHANGED
@@ -64,7 +64,7 @@ class RedisClient
|
|
64
64
|
def scan(*args, **kwargs, &block)
|
65
65
|
return to_enum(__callee__, *args, **kwargs) unless block_given?
|
66
66
|
|
67
|
-
command = @command_builder.generate(['
|
67
|
+
command = @command_builder.generate(['scan', ZERO_CURSOR_FOR_SCAN] + args, kwargs)
|
68
68
|
seed = Random.new_seed
|
69
69
|
loop do
|
70
70
|
cursor, keys = router.scan(command, seed: seed)
|
@@ -77,21 +77,21 @@ class RedisClient
|
|
77
77
|
def sscan(key, *args, **kwargs, &block)
|
78
78
|
return to_enum(__callee__, key, *args, **kwargs) unless block_given?
|
79
79
|
|
80
|
-
command = @command_builder.generate(['
|
80
|
+
command = @command_builder.generate(['sscan', key, ZERO_CURSOR_FOR_SCAN] + args, kwargs)
|
81
81
|
router.scan_single_key(command, arity: 1, &block)
|
82
82
|
end
|
83
83
|
|
84
84
|
def hscan(key, *args, **kwargs, &block)
|
85
85
|
return to_enum(__callee__, key, *args, **kwargs) unless block_given?
|
86
86
|
|
87
|
-
command = @command_builder.generate(['
|
87
|
+
command = @command_builder.generate(['hscan', key, ZERO_CURSOR_FOR_SCAN] + args, kwargs)
|
88
88
|
router.scan_single_key(command, arity: 2, &block)
|
89
89
|
end
|
90
90
|
|
91
91
|
def zscan(key, *args, **kwargs, &block)
|
92
92
|
return to_enum(__callee__, key, *args, **kwargs) unless block_given?
|
93
93
|
|
94
|
-
command = @command_builder.generate(['
|
94
|
+
command = @command_builder.generate(['zscan', key, ZERO_CURSOR_FOR_SCAN] + args, kwargs)
|
95
95
|
router.scan_single_key(command, arity: 2, &block)
|
96
96
|
end
|
97
97
|
|
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.13.
|
4
|
+
version: 0.13.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Taishi Kasuga
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-12-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: redis-client
|