redis-cluster-client 0.13.0 → 0.13.1

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: 2bc3348020af7012ab193ef18128f2cd37028c9225f1e7fa9555d3609ff92362
4
+ data.tar.gz: 3c08890f1c50f297f0d60f0503bd268742bbcd242a32ac73df733496a013660e
5
5
  SHA512:
6
- metadata.gz: 4630e1a9819484cf9675b4d6b77bcf39d437cd821ff3875c5717cfaccc827fc8482f71a6da41e5032227a02b19c214c339956d865ffa942eb8f901d329f3ffea
7
- data.tar.gz: 3e2a2424421543b08623e76dcea06d2a646c3cfadbace64b6dce0adfd8bead2b24d78aa568d1ee36c45bc80c3cdcf473c7fa74e7f30976b0005f1d34791d6707
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[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,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
- 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
+ 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)) 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
 
@@ -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, 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
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 try_send(node, method, command, args, retry_count: 3, &block)
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
- raise ::RedisClient::Cluster::OrchestrationCommandNotSupported.from_command(['cluster', command[1]]).with_config(@config)
385
+ fail_not_supported_command(method, command, args, &block)
366
386
  elsif command[1].casecmp('delslots').zero?
367
- raise ::RedisClient::Cluster::OrchestrationCommandNotSupported.from_command(['cluster', command[1]]).with_config(@config)
387
+ fail_not_supported_command(method, command, args, &block)
368
388
  elsif command[1].casecmp('failover').zero?
369
- raise ::RedisClient::Cluster::OrchestrationCommandNotSupported.from_command(['cluster', command[1]]).with_config(@config)
389
+ fail_not_supported_command(method, command, args, &block)
370
390
  elsif command[1].casecmp('forget').zero?
371
- raise ::RedisClient::Cluster::OrchestrationCommandNotSupported.from_command(['cluster', command[1]]).with_config(@config)
391
+ fail_not_supported_command(method, command, args, &block)
372
392
  elsif command[1].casecmp('meet').zero?
373
- raise ::RedisClient::Cluster::OrchestrationCommandNotSupported.from_command(['cluster', command[1]]).with_config(@config)
393
+ fail_not_supported_command(method, command, args, &block)
374
394
  elsif command[1].casecmp('replicate').zero?
375
- raise ::RedisClient::Cluster::OrchestrationCommandNotSupported.from_command(['cluster', command[1]]).with_config(@config)
395
+ fail_not_supported_command(method, command, args, &block)
376
396
  elsif command[1].casecmp('reset').zero?
377
- raise ::RedisClient::Cluster::OrchestrationCommandNotSupported.from_command(['cluster', command[1]]).with_config(@config)
397
+ fail_not_supported_command(method, command, args, &block)
378
398
  elsif command[1].casecmp('set-config-epoch').zero?
379
- raise ::RedisClient::Cluster::OrchestrationCommandNotSupported.from_command(['cluster', command[1]]).with_config(@config)
399
+ fail_not_supported_command(method, command, args, &block)
380
400
  elsif command[1].casecmp('setslot').zero?
381
- raise ::RedisClient::Cluster::OrchestrationCommandNotSupported.from_command(['cluster', command[1]]).with_config(@config)
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(cmd, method, command, args, &block) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
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 try_send(assign_node(command), method, command, args, &block) if command.size <= keys_step + 1 || ::RedisClient::Cluster::KeySlotConverter.hash_tag_included?(command[1])
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)
@@ -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(['SCAN', ZERO_CURSOR_FOR_SCAN] + args, kwargs)
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(['SSCAN', key, ZERO_CURSOR_FOR_SCAN] + args, kwargs)
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(['HSCAN', key, ZERO_CURSOR_FOR_SCAN] + args, kwargs)
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(['ZSCAN', key, ZERO_CURSOR_FOR_SCAN] + args, kwargs)
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
 
@@ -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,14 @@
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.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-20 00:00:00.000000000 Z
11
+ date: 2024-12-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: redis-client