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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8aa9148e1dedc8ccb5087590b5b693ff2d32ab1b4a16bf25227ab0d030bea56c
4
- data.tar.gz: f8d699a826f263cb5ff1d99f08fd1be1cec551f4306759e14165f800a7ffec57
3
+ metadata.gz: 96ad21a33077fd4809c93c017d75c5f0c257a666a741f329c1e61cf55f6c1cfe
4
+ data.tar.gz: 11c6d1f81a0b529682887266c0b1f0cfec305aff1f289b6d3a7b9aff6ed2151c
5
5
  SHA512:
6
- metadata.gz: 4630e1a9819484cf9675b4d6b77bcf39d437cd821ff3875c5717cfaccc827fc8482f71a6da41e5032227a02b19c214c339956d865ffa942eb8f901d329f3ffea
7
- data.tar.gz: 3e2a2424421543b08623e76dcea06d2a646c3cfadbace64b6dce0adfd8bead2b24d78aa568d1ee36c45bc80c3cdcf473c7fa74e7f30976b0005f1d34791d6707
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[0].nil?
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: row[3],
68
+ first_key_position: pos,
55
69
  key_step: row[5],
56
- write?: row[2].include?('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
- if command.first.casecmp('get').zero?
94
- find_command_info(command.first)&.first_key_position.to_i
95
- elsif command.first.casecmp('mget').zero?
96
- find_command_info(command.first)&.first_key_position.to_i
97
- elsif command.first.casecmp('set').zero?
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 command.first.casecmp('xreadgroup').zero?
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
- find_command_info(command.first)&.first_key_position.to_i
120
+ i
123
121
  end
124
122
  end
125
123
 
126
124
  def determine_optional_key_position(command, option_name)
127
- idx = command.map { |e| e.to_s.downcase(:ascii) }.index(option_name)
128
- idx.nil? ? 0 : idx + 1
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)) do |e, a|
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.try_send(node, method, command, args)
286
+ @router.send_command_to_node(node, method, command, args)
287
287
  else
288
- @router.try_send(node, method, command, args, &block)
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, Metrics/MethodLength
40
- cmd_name = command.first
41
- if cmd_name.casecmp('get').zero?
42
- node = assign_node(command)
43
- try_send(node, method, command, args, &block)
44
- elsif cmd_name.casecmp('mget').zero?
45
- send_multiple_keys_command(command.first, method, command, args, &block)
46
- elsif cmd_name.casecmp('set').zero?
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 try_send(node, method, command, args, retry_count: 3, &block)
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
- raise ::RedisClient::Cluster::OrchestrationCommandNotSupported.from_command(['cluster', command[1]]).with_config(@config)
376
+ fail_not_supported_command(method, command, args, &block)
366
377
  elsif command[1].casecmp('delslots').zero?
367
- raise ::RedisClient::Cluster::OrchestrationCommandNotSupported.from_command(['cluster', command[1]]).with_config(@config)
378
+ fail_not_supported_command(method, command, args, &block)
368
379
  elsif command[1].casecmp('failover').zero?
369
- raise ::RedisClient::Cluster::OrchestrationCommandNotSupported.from_command(['cluster', command[1]]).with_config(@config)
380
+ fail_not_supported_command(method, command, args, &block)
370
381
  elsif command[1].casecmp('forget').zero?
371
- raise ::RedisClient::Cluster::OrchestrationCommandNotSupported.from_command(['cluster', command[1]]).with_config(@config)
382
+ fail_not_supported_command(method, command, args, &block)
372
383
  elsif command[1].casecmp('meet').zero?
373
- raise ::RedisClient::Cluster::OrchestrationCommandNotSupported.from_command(['cluster', command[1]]).with_config(@config)
384
+ fail_not_supported_command(method, command, args, &block)
374
385
  elsif command[1].casecmp('replicate').zero?
375
- raise ::RedisClient::Cluster::OrchestrationCommandNotSupported.from_command(['cluster', command[1]]).with_config(@config)
386
+ fail_not_supported_command(method, command, args, &block)
376
387
  elsif command[1].casecmp('reset').zero?
377
- raise ::RedisClient::Cluster::OrchestrationCommandNotSupported.from_command(['cluster', command[1]]).with_config(@config)
388
+ fail_not_supported_command(method, command, args, &block)
378
389
  elsif command[1].casecmp('set-config-epoch').zero?
379
- raise ::RedisClient::Cluster::OrchestrationCommandNotSupported.from_command(['cluster', command[1]]).with_config(@config)
390
+ fail_not_supported_command(method, command, args, &block)
380
391
  elsif command[1].casecmp('setslot').zero?
381
- raise ::RedisClient::Cluster::OrchestrationCommandNotSupported.from_command(['cluster', command[1]]).with_config(@config)
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(cmd, method, command, args, &block) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
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 try_send(assign_node(command), method, command, args, &block) if command.size <= keys_step + 1 || ::RedisClient::Cluster::KeySlotConverter.hash_tag_included?(command[1])
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)
@@ -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(['SCAN', ZERO_CURSOR_FOR_SCAN] + args, kwargs)
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(['SSCAN', key, ZERO_CURSOR_FOR_SCAN] + args, kwargs)
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(['HSCAN', key, ZERO_CURSOR_FOR_SCAN] + args, kwargs)
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(['ZSCAN', key, ZERO_CURSOR_FOR_SCAN] + args, kwargs)
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
 
@@ -47,7 +47,6 @@ class RedisClient
47
47
  max_startup_sample: MAX_STARTUP_SAMPLE,
48
48
  **client_config
49
49
  )
50
-
51
50
  @replica = true & replica
52
51
  @replica_affinity = replica_affinity.to_s.to_sym
53
52
  @fixed_hostname = fixed_hostname.to_s
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.0
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: 2024-11-20 00:00:00.000000000 Z
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.5.22
81
- signing_key:
77
+ rubygems_version: 3.6.2
82
78
  specification_version: 4
83
- summary: A Redis cluster client for Ruby
79
+ summary: Redis cluster-aware client for Ruby
84
80
  test_files: []