redis-cluster-client 0.13.0 → 0.13.2
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 +31 -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 +102 -90
- data/lib/redis_client/cluster.rb +4 -5
- data/lib/redis_client/cluster_config.rb +0 -1
- metadata +4 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 96ad21a33077fd4809c93c017d75c5f0c257a666a741f329c1e61cf55f6c1cfe
|
4
|
+
data.tar.gz: 11c6d1f81a0b529682887266c0b1f0cfec305aff1f289b6d3a7b9aff6ed2151c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0fa59538e10f1b37b9ef4c02173b4a3a58baf7abc41cd67d18c18f94fad236aac5ffa0e51bfffc33a17a8a116f0c49aef3744b54f91f9a032a7c57d04a0d0c38
|
7
|
+
data.tar.gz: 858ade5da894495cd5c72b036be79ba58775a78ce21184d3d0425d2ad30e2d73ad501a9580bbf1ed658eebe04fac8a24d8b9b2e2b8c37d719012df0ae7c027fa
|
@@ -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,26 @@ 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
|
+
i = command.index { |v| v.to_s.casecmp(option_name).zero? }
|
126
|
+
i.nil? ? 0 : i + 1
|
129
127
|
end
|
130
128
|
end
|
131
129
|
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
|
|
@@ -17,8 +17,54 @@ class RedisClient
|
|
17
17
|
class Router
|
18
18
|
ZERO_CURSOR_FOR_SCAN = '0'
|
19
19
|
TSF = ->(f, x) { f.nil? ? x : f.call(x) }.curry
|
20
|
+
DEDICATED_ACTIONS = lambda do # rubocop:disable Metrics/BlockLength
|
21
|
+
action = Struct.new('RedisCommandRoutingAction', :method_name, :reply_transformer, keyword_init: true)
|
22
|
+
pick_first = ->(reply) { reply.first } # rubocop:disable Style/SymbolProc
|
23
|
+
multiple_key_action = action.new(method_name: :send_multiple_keys_command)
|
24
|
+
all_node_first_action = action.new(method_name: :send_command_to_all_nodes, reply_transformer: pick_first)
|
25
|
+
primary_first_action = action.new(method_name: :send_command_to_primaries, reply_transformer: pick_first)
|
26
|
+
not_supported_action = action.new(method_name: :fail_not_supported_command)
|
27
|
+
keyless_action = action.new(method_name: :fail_keyless_command)
|
28
|
+
{
|
29
|
+
'ping' => action.new(method_name: :send_ping_command, reply_transformer: pick_first),
|
30
|
+
'wait' => action.new(method_name: :send_wait_command),
|
31
|
+
'keys' => action.new(method_name: :send_command_to_replicas, reply_transformer: ->(reply) { reply.flatten.sort_by(&:to_s) }),
|
32
|
+
'dbsize' => action.new(method_name: :send_command_to_replicas, reply_transformer: ->(reply) { reply.select { |e| e.is_a?(Integer) }.sum }),
|
33
|
+
'scan' => action.new(method_name: :send_scan_command),
|
34
|
+
'lastsave' => action.new(method_name: :send_command_to_all_nodes, reply_transformer: ->(reply) { reply.sort_by(&:to_i) }),
|
35
|
+
'role' => action.new(method_name: :send_command_to_all_nodes),
|
36
|
+
'config' => action.new(method_name: :send_config_command),
|
37
|
+
'client' => action.new(method_name: :send_client_command),
|
38
|
+
'cluster' => action.new(method_name: :send_cluster_command),
|
39
|
+
'memory' => action.new(method_name: :send_memory_command),
|
40
|
+
'script' => action.new(method_name: :send_script_command),
|
41
|
+
'pubsub' => action.new(method_name: :send_pubsub_command),
|
42
|
+
'watch' => action.new(method_name: :send_watch_command),
|
43
|
+
'mget' => multiple_key_action,
|
44
|
+
'mset' => multiple_key_action,
|
45
|
+
'del' => multiple_key_action,
|
46
|
+
'acl' => all_node_first_action,
|
47
|
+
'auth' => all_node_first_action,
|
48
|
+
'bgrewriteaof' => all_node_first_action,
|
49
|
+
'bgsave' => all_node_first_action,
|
50
|
+
'quit' => all_node_first_action,
|
51
|
+
'save' => all_node_first_action,
|
52
|
+
'flushall' => primary_first_action,
|
53
|
+
'flushdb' => primary_first_action,
|
54
|
+
'readonly' => not_supported_action,
|
55
|
+
'readwrite' => not_supported_action,
|
56
|
+
'shutdown' => not_supported_action,
|
57
|
+
'discard' => keyless_action,
|
58
|
+
'exec' => keyless_action,
|
59
|
+
'multi' => keyless_action,
|
60
|
+
'unwatch' => keyless_action
|
61
|
+
}.each_with_object({}) do |(k, v), acc|
|
62
|
+
acc[k] = v
|
63
|
+
acc[k.upcase] = v
|
64
|
+
end
|
65
|
+
end.call.freeze
|
20
66
|
|
21
|
-
private_constant :ZERO_CURSOR_FOR_SCAN, :TSF
|
67
|
+
private_constant :ZERO_CURSOR_FOR_SCAN, :TSF, :DEDICATED_ACTIONS
|
22
68
|
|
23
69
|
attr_reader :config
|
24
70
|
|
@@ -36,82 +82,14 @@ class RedisClient
|
|
36
82
|
raise
|
37
83
|
end
|
38
84
|
|
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
|
85
|
+
def send_command(method, command, *args, &block) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
86
|
+
return assign_node_and_send_command(method, command, args, &block) unless DEDICATED_ACTIONS.key?(command.first)
|
87
|
+
|
88
|
+
action = DEDICATED_ACTIONS[command.first]
|
89
|
+
return send(action.method_name, method, command, args, &block) if action.reply_transformer.nil?
|
90
|
+
|
91
|
+
reply = send(action.method_name, method, command, args)
|
92
|
+
action.reply_transformer.call(reply).then(&TSF.call(block))
|
115
93
|
rescue ::RedisClient::CircuitBreaker::OpenCircuitError
|
116
94
|
raise
|
117
95
|
rescue ::RedisClient::Cluster::Node::ReloadNeeded
|
@@ -137,7 +115,12 @@ class RedisClient
|
|
137
115
|
end
|
138
116
|
|
139
117
|
# @see https://redis.io/docs/reference/cluster-spec/#redirection-and-resharding Redirection and resharding
|
140
|
-
def
|
118
|
+
def assign_node_and_send_command(method, command, args, retry_count: 3, &block)
|
119
|
+
node = assign_node(command)
|
120
|
+
send_command_to_node(node, method, command, args, retry_count: retry_count, &block)
|
121
|
+
end
|
122
|
+
|
123
|
+
def send_command_to_node(node, method, command, args, retry_count: 3, &block)
|
141
124
|
handle_redirection(node, command, retry_count: retry_count) do |on_node|
|
142
125
|
if args.empty?
|
143
126
|
# prevent memory allocation for variable-length args
|
@@ -312,6 +295,34 @@ class RedisClient
|
|
312
295
|
|
313
296
|
private
|
314
297
|
|
298
|
+
def send_command_to_all_nodes(method, command, args, &block)
|
299
|
+
@node.call_all(method, command, args, &block)
|
300
|
+
end
|
301
|
+
|
302
|
+
def send_command_to_primaries(method, command, args, &block)
|
303
|
+
@node.call_primaries(method, command, args, &block)
|
304
|
+
end
|
305
|
+
|
306
|
+
def send_command_to_replicas(method, command, args, &block)
|
307
|
+
@node.call_replicas(method, command, args, &block)
|
308
|
+
end
|
309
|
+
|
310
|
+
def send_ping_command(method, command, args, &block)
|
311
|
+
@node.send_ping(method, command, args, &block)
|
312
|
+
end
|
313
|
+
|
314
|
+
def send_scan_command(_method, command, _args, &_block)
|
315
|
+
scan(command, seed: 1)
|
316
|
+
end
|
317
|
+
|
318
|
+
def fail_not_supported_command(_method, command, _args, &_block)
|
319
|
+
raise ::RedisClient::Cluster::OrchestrationCommandNotSupported.from_command(command.first).with_config(@config)
|
320
|
+
end
|
321
|
+
|
322
|
+
def fail_keyless_command(_method, command, _args, &_block)
|
323
|
+
raise ::RedisClient::Cluster::AmbiguousNodeError.from_command(command.first).with_config(@config)
|
324
|
+
end
|
325
|
+
|
315
326
|
def send_wait_command(method, command, args, retry_count: 1, &block) # rubocop:disable Metrics/AbcSize
|
316
327
|
@node.call_primaries(method, command, args).select { |r| r.is_a?(Integer) }.sum.then(&TSF.call(block))
|
317
328
|
rescue ::RedisClient::Cluster::ErrorCollection => e
|
@@ -362,23 +373,23 @@ class RedisClient
|
|
362
373
|
|
363
374
|
def send_cluster_command(method, command, args, &block) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
364
375
|
if command[1].casecmp('addslots').zero?
|
365
|
-
|
376
|
+
fail_not_supported_command(method, command, args, &block)
|
366
377
|
elsif command[1].casecmp('delslots').zero?
|
367
|
-
|
378
|
+
fail_not_supported_command(method, command, args, &block)
|
368
379
|
elsif command[1].casecmp('failover').zero?
|
369
|
-
|
380
|
+
fail_not_supported_command(method, command, args, &block)
|
370
381
|
elsif command[1].casecmp('forget').zero?
|
371
|
-
|
382
|
+
fail_not_supported_command(method, command, args, &block)
|
372
383
|
elsif command[1].casecmp('meet').zero?
|
373
|
-
|
384
|
+
fail_not_supported_command(method, command, args, &block)
|
374
385
|
elsif command[1].casecmp('replicate').zero?
|
375
|
-
|
386
|
+
fail_not_supported_command(method, command, args, &block)
|
376
387
|
elsif command[1].casecmp('reset').zero?
|
377
|
-
|
388
|
+
fail_not_supported_command(method, command, args, &block)
|
378
389
|
elsif command[1].casecmp('set-config-epoch').zero?
|
379
|
-
|
390
|
+
fail_not_supported_command(method, command, args, &block)
|
380
391
|
elsif command[1].casecmp('setslot').zero?
|
381
|
-
|
392
|
+
fail_not_supported_command(method, command, args, &block)
|
382
393
|
elsif command[1].casecmp('saveconfig').zero?
|
383
394
|
@node.call_all(method, command, args).first.then(&TSF.call(block))
|
384
395
|
elsif command[1].casecmp('getkeysinslot').zero?
|
@@ -427,7 +438,7 @@ class RedisClient
|
|
427
438
|
end
|
428
439
|
end
|
429
440
|
|
430
|
-
def send_watch_command(command)
|
441
|
+
def send_watch_command(_method, command, _args, &_block)
|
431
442
|
unless block_given?
|
432
443
|
msg = 'A block required. And you need to use the block argument as a client for the transaction.'
|
433
444
|
raise ::RedisClient::Cluster::Transaction::ConsistencyError.new(msg).with_config(@config)
|
@@ -442,8 +453,9 @@ class RedisClient
|
|
442
453
|
end
|
443
454
|
end
|
444
455
|
|
445
|
-
def send_multiple_keys_command(
|
456
|
+
def send_multiple_keys_command(method, command, args, &block) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
446
457
|
# This implementation is prioritized performance rather than readability or so.
|
458
|
+
cmd = command.first
|
447
459
|
if cmd.casecmp('mget').zero?
|
448
460
|
single_key_cmd = 'get'
|
449
461
|
keys_step = 1
|
@@ -457,7 +469,7 @@ class RedisClient
|
|
457
469
|
raise NotImplementedError, cmd
|
458
470
|
end
|
459
471
|
|
460
|
-
return
|
472
|
+
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
473
|
|
462
474
|
seed = @config.use_replica? && @config.replica_affinity == :random ? nil : Random.new_seed
|
463
475
|
pipeline = ::RedisClient::Cluster::Pipeline.new(self, @command_builder, @concurrent_worker, exception: true, seed: seed)
|
data/lib/redis_client/cluster.rb
CHANGED
@@ -19,7 +19,6 @@ class RedisClient
|
|
19
19
|
@config = config.nil? ? ClusterConfig.new(**kwargs) : config
|
20
20
|
@concurrent_worker = ::RedisClient::Cluster::ConcurrentWorker.create(**(concurrency || {}))
|
21
21
|
@command_builder = @config.command_builder
|
22
|
-
|
23
22
|
@pool = pool
|
24
23
|
@kwargs = kwargs
|
25
24
|
@router = nil
|
@@ -64,7 +63,7 @@ class RedisClient
|
|
64
63
|
def scan(*args, **kwargs, &block)
|
65
64
|
return to_enum(__callee__, *args, **kwargs) unless block_given?
|
66
65
|
|
67
|
-
command = @command_builder.generate(['
|
66
|
+
command = @command_builder.generate(['scan', ZERO_CURSOR_FOR_SCAN] + args, kwargs)
|
68
67
|
seed = Random.new_seed
|
69
68
|
loop do
|
70
69
|
cursor, keys = router.scan(command, seed: seed)
|
@@ -77,21 +76,21 @@ class RedisClient
|
|
77
76
|
def sscan(key, *args, **kwargs, &block)
|
78
77
|
return to_enum(__callee__, key, *args, **kwargs) unless block_given?
|
79
78
|
|
80
|
-
command = @command_builder.generate(['
|
79
|
+
command = @command_builder.generate(['sscan', key, ZERO_CURSOR_FOR_SCAN] + args, kwargs)
|
81
80
|
router.scan_single_key(command, arity: 1, &block)
|
82
81
|
end
|
83
82
|
|
84
83
|
def hscan(key, *args, **kwargs, &block)
|
85
84
|
return to_enum(__callee__, key, *args, **kwargs) unless block_given?
|
86
85
|
|
87
|
-
command = @command_builder.generate(['
|
86
|
+
command = @command_builder.generate(['hscan', key, ZERO_CURSOR_FOR_SCAN] + args, kwargs)
|
88
87
|
router.scan_single_key(command, arity: 2, &block)
|
89
88
|
end
|
90
89
|
|
91
90
|
def zscan(key, *args, **kwargs, &block)
|
92
91
|
return to_enum(__callee__, key, *args, **kwargs) unless block_given?
|
93
92
|
|
94
|
-
command = @command_builder.generate(['
|
93
|
+
command = @command_builder.generate(['zscan', key, ZERO_CURSOR_FOR_SCAN] + args, kwargs)
|
95
94
|
router.scan_single_key(command, arity: 2, &block)
|
96
95
|
end
|
97
96
|
|
metadata
CHANGED
@@ -1,14 +1,13 @@
|
|
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.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Taishi Kasuga
|
8
|
-
autorequire:
|
9
8
|
bindir: bin
|
10
9
|
cert_chain: []
|
11
|
-
date:
|
10
|
+
date: 2025-01-16 00:00:00.000000000 Z
|
12
11
|
dependencies:
|
13
12
|
- !ruby/object:Gem::Dependency
|
14
13
|
name: redis-client
|
@@ -24,7 +23,6 @@ dependencies:
|
|
24
23
|
- - "~>"
|
25
24
|
- !ruby/object:Gem::Version
|
26
25
|
version: '0.22'
|
27
|
-
description:
|
28
26
|
email:
|
29
27
|
- proxy0721@gmail.com
|
30
28
|
executables: []
|
@@ -62,7 +60,6 @@ licenses:
|
|
62
60
|
metadata:
|
63
61
|
rubygems_mfa_required: 'true'
|
64
62
|
allowed_push_host: https://rubygems.org
|
65
|
-
post_install_message:
|
66
63
|
rdoc_options: []
|
67
64
|
require_paths:
|
68
65
|
- lib
|
@@ -77,8 +74,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
77
74
|
- !ruby/object:Gem::Version
|
78
75
|
version: '0'
|
79
76
|
requirements: []
|
80
|
-
rubygems_version: 3.
|
81
|
-
signing_key:
|
77
|
+
rubygems_version: 3.6.2
|
82
78
|
specification_version: 4
|
83
|
-
summary:
|
79
|
+
summary: Redis cluster-aware client for Ruby
|
84
80
|
test_files: []
|