redis-cluster-client 0.13.0 → 0.13.2

Sign up to get free protection for your applications and to get access to all the features.
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: []