redis-cluster-client 0.13.0 → 0.13.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml 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