redis-cluster-client 0.12.1 → 0.13.0

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: 5d90c095f5058e808331eaa9219fb52b9d4a27de639a9c54d194c68ecb253611
4
- data.tar.gz: 8cd8092db87c7cfa42e00c6cabd30747bdea74130d8eb1e19b971b95637665fa
3
+ metadata.gz: 8aa9148e1dedc8ccb5087590b5b693ff2d32ab1b4a16bf25227ab0d030bea56c
4
+ data.tar.gz: f8d699a826f263cb5ff1d99f08fd1be1cec551f4306759e14165f800a7ffec57
5
5
  SHA512:
6
- metadata.gz: 4821b78b0d5766566fa3943f65271908d3e52e3f82269bd2ea0a789e571df7818b5e0275c88d8833eb7b449c7c0eb3ed672034c1caf03fac35528114f31adb73
7
- data.tar.gz: a4bb1d4a766cd742645c61342c2822e2bd337c792fb67b9793bfbb06ec0a889185eaa8b3ed34f96591f9e57997ecfe83cc901ce65451577b7e8f13d7d23f64b3
6
+ metadata.gz: 4630e1a9819484cf9675b4d6b77bcf39d437cd821ff3875c5717cfaccc827fc8482f71a6da41e5032227a02b19c214c339956d865ffa942eb8f901d329f3ffea
7
+ data.tar.gz: 3e2a2424421543b08623e76dcea06d2a646c3cfadbace64b6dce0adfd8bead2b24d78aa568d1ee36c45bc80c3cdcf473c7fa74e7f30976b0005f1d34791d6707
@@ -3,7 +3,6 @@
3
3
  require 'redis_client'
4
4
  require 'redis_client/cluster/errors'
5
5
  require 'redis_client/cluster/key_slot_converter'
6
- require 'redis_client/cluster/normalized_cmd_name'
7
6
 
8
7
  class RedisClient
9
8
  class Cluster
@@ -30,7 +29,7 @@ class RedisClient
30
29
  nodes&.each do |node|
31
30
  regular_timeout = node.read_timeout
32
31
  node.read_timeout = slow_command_timeout > 0.0 ? slow_command_timeout : regular_timeout
33
- reply = node.call('COMMAND')
32
+ reply = node.call('command')
34
33
  node.read_timeout = regular_timeout
35
34
  commands = parse_command_reply(reply)
36
35
  cmd = ::RedisClient::Cluster::Command.new(commands)
@@ -51,7 +50,7 @@ class RedisClient
51
50
  rows&.each_with_object({}) do |row, acc|
52
51
  next if row[0].nil?
53
52
 
54
- acc[row[0].downcase] = ::RedisClient::Cluster::Command::Detail.new(
53
+ acc[row.first] = ::RedisClient::Cluster::Command::Detail.new(
55
54
  first_key_position: row[3],
56
55
  key_step: row[5],
57
56
  write?: row[2].include?('write'),
@@ -73,38 +72,59 @@ class RedisClient
73
72
  end
74
73
 
75
74
  def should_send_to_primary?(command)
76
- name = ::RedisClient::Cluster::NormalizedCmdName.instance.get_by_command(command)
77
- @commands[name]&.write?
75
+ find_command_info(command.first)&.write?
78
76
  end
79
77
 
80
78
  def should_send_to_replica?(command)
81
- name = ::RedisClient::Cluster::NormalizedCmdName.instance.get_by_command(command)
82
- @commands[name]&.readonly?
79
+ find_command_info(command.first)&.readonly?
83
80
  end
84
81
 
85
82
  def exists?(name)
86
- @commands.key?(::RedisClient::Cluster::NormalizedCmdName.instance.get_by_name(name))
83
+ @commands.key?(name) || @commands.key?(name.to_s.downcase(:ascii))
87
84
  end
88
85
 
89
86
  private
90
87
 
91
- def determine_first_key_position(command) # rubocop:disable Metrics/CyclomaticComplexity
92
- case name = ::RedisClient::Cluster::NormalizedCmdName.instance.get_by_command(command)
93
- when 'eval', 'evalsha', 'zinterstore', 'zunionstore' then 3
94
- when 'object' then 2
95
- when 'memory'
88
+ def find_command_info(name)
89
+ @commands[name] || @commands[name.to_s.downcase(:ascii)]
90
+ end
91
+
92
+ 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?
96
114
  command[1].to_s.casecmp('usage').zero? ? 2 : 0
97
- when 'migrate'
115
+ elsif command.first.casecmp('migrate').zero?
98
116
  command[3].empty? ? determine_optional_key_position(command, 'keys') : 3
99
- when 'xread', 'xreadgroup'
117
+ elsif command.first.casecmp('xread').zero?
118
+ determine_optional_key_position(command, 'streams')
119
+ elsif command.first.casecmp('xreadgroup').zero?
100
120
  determine_optional_key_position(command, 'streams')
101
121
  else
102
- @commands[name]&.first_key_position.to_i
122
+ find_command_info(command.first)&.first_key_position.to_i
103
123
  end
104
124
  end
105
125
 
106
126
  def determine_optional_key_position(command, option_name)
107
- idx = command.map { |e| e.to_s.downcase }.index(option_name&.downcase)
127
+ idx = command.map { |e| e.to_s.downcase(:ascii) }.index(option_name)
108
128
  idx.nil? ? 0 : idx + 1
109
129
  end
110
130
  end
@@ -5,6 +5,8 @@ class RedisClient
5
5
  module ConcurrentWorker
6
6
  class OnDemand
7
7
  def initialize(size:)
8
+ raise ArgumentError, "size must be positive: #{size}" unless size.positive?
9
+
8
10
  @q = SizedQueue.new(size)
9
11
  end
10
12
 
@@ -11,6 +11,8 @@ class RedisClient
11
11
  # So it consumes memory 1 MB multiplied a number of workers.
12
12
  class Pooled
13
13
  def initialize(size:)
14
+ raise ArgumentError, "size must be positive: #{size}" unless size.positive?
15
+
14
16
  @size = size
15
17
  setup
16
18
  end
@@ -71,14 +71,12 @@ class RedisClient
71
71
 
72
72
  module_function
73
73
 
74
- def create(model: :on_demand, size: 5)
75
- size = size.positive? ? size : 5
76
-
74
+ def create(model: :none, size: 5)
77
75
  case model
78
- when :on_demand, nil then ::RedisClient::Cluster::ConcurrentWorker::OnDemand.new(size: size)
79
- when :pooled then ::RedisClient::Cluster::ConcurrentWorker::Pooled.new(size: size)
80
76
  when :none then ::RedisClient::Cluster::ConcurrentWorker::None.new
81
- else raise ArgumentError, "Unknown model: #{model}"
77
+ when :on_demand then ::RedisClient::Cluster::ConcurrentWorker::OnDemand.new(size: size)
78
+ when :pooled then ::RedisClient::Cluster::ConcurrentWorker::Pooled.new(size: size)
79
+ else raise ArgumentError, "unknown model: #{model}"
82
80
  end
83
81
  end
84
82
  end
@@ -43,11 +43,16 @@ class RedisClient
43
43
  if !errors.is_a?(Hash) || errors.empty?
44
44
  new(errors.to_s).with_errors(EMPTY_HASH)
45
45
  else
46
- messages = errors.map { |node_key, error| "#{node_key}: (#{error.class}) #{error.message}" }
46
+ messages = errors.map { |node_key, error| "#{node_key}: (#{error.class}) #{error.message}" }.freeze
47
47
  new(messages.join(', ')).with_errors(errors)
48
48
  end
49
49
  end
50
50
 
51
+ def initialize(error_message = nil)
52
+ @errors = nil
53
+ super
54
+ end
55
+
51
56
  def with_errors(errors)
52
57
  @errors = errors if @errors.nil?
53
58
  self
@@ -61,7 +66,7 @@ class RedisClient
61
66
  end
62
67
 
63
68
  class NodeMightBeDown < Error
64
- def initialize(_ = '')
69
+ def initialize(_error_message = nil)
65
70
  super(
66
71
  'The client is trying to fetch the latest cluster state ' \
67
72
  'because a subset of nodes might be down. ' \
@@ -47,7 +47,7 @@ class RedisClient
47
47
  min = DUMMY_LATENCY_MSEC
48
48
  MEASURE_ATTEMPT_COUNT.times do
49
49
  starting = obtain_current_time
50
- cli.call_once('PING')
50
+ cli.call_once('ping')
51
51
  duration = obtain_current_time - starting
52
52
  min = duration if duration < min
53
53
  end
@@ -90,7 +90,7 @@ class RedisClient
90
90
 
91
91
  def build_connection_prelude
92
92
  prelude = super.dup
93
- prelude << ['READONLY'] if @scale_read
93
+ prelude << ['readonly'] if @scale_read
94
94
  prelude.freeze
95
95
  end
96
96
  end
@@ -309,7 +309,7 @@ class RedisClient
309
309
  work_group.push(i, raw_client) do |client|
310
310
  regular_timeout = client.read_timeout
311
311
  client.read_timeout = @config.slow_command_timeout > 0.0 ? @config.slow_command_timeout : regular_timeout
312
- reply = client.call_once('CLUSTER', 'NODES')
312
+ reply = client.call_once('cluster', 'nodes')
313
313
  client.read_timeout = regular_timeout
314
314
  parse_cluster_node_reply(reply)
315
315
  rescue StandardError => e
@@ -18,15 +18,15 @@ class RedisClient
18
18
  handle_redirection(slot, retry_count: 1) do |nd|
19
19
  nd.with do |c|
20
20
  c.ensure_connected_cluster_scoped(retryable: false) do
21
- c.call('ASKING') if @asking
22
- c.call('WATCH', *keys)
21
+ c.call('asking') if @asking
22
+ c.call('watch', *keys)
23
23
  begin
24
24
  yield(c, slot, @asking)
25
25
  rescue ::RedisClient::ConnectionError
26
26
  # No need to unwatch on a connection error.
27
27
  raise
28
28
  rescue StandardError
29
- c.call('UNWATCH')
29
+ c.call('unwatch')
30
30
  raise
31
31
  end
32
32
  rescue ::RedisClient::CommandError => e
@@ -290,7 +290,7 @@ class RedisClient
290
290
  end
291
291
 
292
292
  def try_asking(node)
293
- node.call('ASKING') == 'OK'
293
+ node.call('asking') == 'OK'
294
294
  rescue StandardError
295
295
  false
296
296
  end
@@ -2,7 +2,6 @@
2
2
 
3
3
  require 'redis_client'
4
4
  require 'redis_client/cluster/errors'
5
- require 'redis_client/cluster/normalized_cmd_name'
6
5
 
7
6
  class RedisClient
8
7
  class Cluster
@@ -108,12 +107,21 @@ class RedisClient
108
107
 
109
108
  private
110
109
 
111
- def _call(command)
112
- case ::RedisClient::Cluster::NormalizedCmdName.instance.get_by_command(command)
113
- when 'subscribe', 'psubscribe', 'ssubscribe' then call_to_single_state(command)
114
- when 'unsubscribe', 'punsubscribe' then call_to_all_states(command)
115
- when 'sunsubscribe' then call_for_sharded_states(command)
116
- else call_to_single_state(command)
110
+ def _call(command) # rubocop:disable Metrics/AbcSize
111
+ if command.first.casecmp('subscribe').zero?
112
+ call_to_single_state(command)
113
+ elsif command.first.casecmp('psubscribe').zero?
114
+ call_to_single_state(command)
115
+ elsif command.first.casecmp('ssubscribe').zero?
116
+ call_to_single_state(command)
117
+ elsif command.first.casecmp('unsubscribe').zero?
118
+ call_to_all_states(command)
119
+ elsif command.first.casecmp('punsubscribe').zero?
120
+ call_to_all_states(command)
121
+ elsif command.first.casecmp('sunsubscribe').zero?
122
+ call_for_sharded_states(command)
123
+ else
124
+ call_to_single_state(command)
117
125
  end
118
126
  end
119
127
 
@@ -7,7 +7,6 @@ require 'redis_client/cluster/errors'
7
7
  require 'redis_client/cluster/key_slot_converter'
8
8
  require 'redis_client/cluster/node'
9
9
  require 'redis_client/cluster/node_key'
10
- require 'redis_client/cluster/normalized_cmd_name'
11
10
  require 'redis_client/cluster/transaction'
12
11
  require 'redis_client/cluster/optimistic_locking'
13
12
  require 'redis_client/cluster/pipeline'
@@ -37,33 +36,78 @@ class RedisClient
37
36
  raise
38
37
  end
39
38
 
40
- def send_command(method, command, *args, &block) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
41
- cmd = ::RedisClient::Cluster::NormalizedCmdName.instance.get_by_command(command)
42
- case cmd
43
- when 'ping' then @node.send_ping(method, command, args).first.then(&TSF.call(block))
44
- when 'wait' then send_wait_command(method, command, args, &block)
45
- when 'keys' then @node.call_replicas(method, command, args).flatten.sort_by(&:to_s).then(&TSF.call(block))
46
- when 'dbsize' then @node.call_replicas(method, command, args).select { |e| e.is_a?(Integer) }.sum.then(&TSF.call(block))
47
- when 'scan' then scan(command, seed: 1)
48
- when 'lastsave' then @node.call_all(method, command, args).sort_by(&:to_i).then(&TSF.call(block))
49
- when 'role' then @node.call_all(method, command, args, &block)
50
- when 'config' then send_config_command(method, command, args, &block)
51
- when 'client' then send_client_command(method, command, args, &block)
52
- when 'cluster' then send_cluster_command(method, command, args, &block)
53
- when 'memory' then send_memory_command(method, command, args, &block)
54
- when 'script' then send_script_command(method, command, args, &block)
55
- when 'pubsub' then send_pubsub_command(method, command, args, &block)
56
- when 'watch' then send_watch_command(command, &block)
57
- when 'mset', 'mget', 'del'
58
- send_multiple_keys_command(cmd, method, command, args, &block)
59
- when 'acl', 'auth', 'bgrewriteaof', 'bgsave', 'quit', 'save'
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?
60
88
  @node.call_all(method, command, args).first.then(&TSF.call(block))
61
- when 'flushall', 'flushdb'
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?
62
96
  @node.call_primaries(method, command, args).first.then(&TSF.call(block))
63
- when 'readonly', 'readwrite', 'shutdown'
64
- raise ::RedisClient::Cluster::OrchestrationCommandNotSupported.from_command(cmd).with_config(@config)
65
- when 'discard', 'exec', 'multi', 'unwatch'
66
- raise ::RedisClient::Cluster::AmbiguousNodeError.from_command(cmd).with_config(@config)
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)
67
111
  else
68
112
  node = assign_node(command)
69
113
  try_send(node, method, command, args, &block)
@@ -118,7 +162,7 @@ class RedisClient
118
162
  elsif e.message.start_with?('ASK')
119
163
  node = assign_asking_node(e.message)
120
164
  if retry_count >= 0
121
- node.call('ASKING')
165
+ node.call('asking')
122
166
  retry
123
167
  end
124
168
  elsif e.message.start_with?('CLUSTERDOWN')
@@ -280,75 +324,106 @@ class RedisClient
280
324
  retry
281
325
  end
282
326
 
283
- def send_config_command(method, command, args, &block)
284
- case ::RedisClient::Cluster::NormalizedCmdName.instance.get_by_subcommand(command)
285
- when 'resetstat', 'rewrite', 'set'
327
+ def send_config_command(method, command, args, &block) # rubocop:disable Metrics/AbcSize
328
+ if command[1].casecmp('resetstat').zero?
286
329
  @node.call_all(method, command, args).first.then(&TSF.call(block))
287
- else assign_node(command).public_send(method, *args, command, &block)
330
+ elsif command[1].casecmp('rewrite').zero?
331
+ @node.call_all(method, command, args).first.then(&TSF.call(block))
332
+ elsif command[1].casecmp('set').zero?
333
+ @node.call_all(method, command, args).first.then(&TSF.call(block))
334
+ else
335
+ assign_node(command).public_send(method, *args, command, &block)
288
336
  end
289
337
  end
290
338
 
291
339
  def send_memory_command(method, command, args, &block)
292
- case ::RedisClient::Cluster::NormalizedCmdName.instance.get_by_subcommand(command)
293
- when 'stats' then @node.call_all(method, command, args, &block)
294
- when 'purge' then @node.call_all(method, command, args).first.then(&TSF.call(block))
295
- else assign_node(command).public_send(method, *args, command, &block)
340
+ if command[1].casecmp('stats').zero?
341
+ @node.call_all(method, command, args, &block)
342
+ elsif command[1].casecmp('purge').zero?
343
+ @node.call_all(method, command, args).first.then(&TSF.call(block))
344
+ else
345
+ assign_node(command).public_send(method, *args, command, &block)
296
346
  end
297
347
  end
298
348
 
299
- def send_client_command(method, command, args, &block)
300
- case ::RedisClient::Cluster::NormalizedCmdName.instance.get_by_subcommand(command)
301
- when 'list' then @node.call_all(method, command, args, &block).flatten
302
- when 'pause', 'reply', 'setname'
349
+ def send_client_command(method, command, args, &block) # rubocop:disable Metrics/AbcSize
350
+ if command[1].casecmp('list').zero?
351
+ @node.call_all(method, command, args, &block).flatten
352
+ elsif command[1].casecmp('pause').zero?
353
+ @node.call_all(method, command, args).first.then(&TSF.call(block))
354
+ elsif command[1].casecmp('reply').zero?
303
355
  @node.call_all(method, command, args).first.then(&TSF.call(block))
304
- else assign_node(command).public_send(method, *args, command, &block)
356
+ elsif command[1].casecmp('setname').zero?
357
+ @node.call_all(method, command, args).first.then(&TSF.call(block))
358
+ else
359
+ assign_node(command).public_send(method, *args, command, &block)
305
360
  end
306
361
  end
307
362
 
308
- def send_cluster_command(method, command, args, &block) # rubocop:disable Metrics/AbcSize
309
- case subcommand = ::RedisClient::Cluster::NormalizedCmdName.instance.get_by_subcommand(command)
310
- when 'addslots', 'delslots', 'failover', 'forget', 'meet', 'replicate',
311
- 'reset', 'set-config-epoch', 'setslot'
312
- raise ::RedisClient::Cluster::OrchestrationCommandNotSupported.from_command(['cluster', subcommand]).with_config(@config)
313
- when 'saveconfig' then @node.call_all(method, command, args).first.then(&TSF.call(block))
314
- when 'getkeysinslot'
363
+ def send_cluster_command(method, command, args, &block) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
364
+ if command[1].casecmp('addslots').zero?
365
+ raise ::RedisClient::Cluster::OrchestrationCommandNotSupported.from_command(['cluster', command[1]]).with_config(@config)
366
+ elsif command[1].casecmp('delslots').zero?
367
+ raise ::RedisClient::Cluster::OrchestrationCommandNotSupported.from_command(['cluster', command[1]]).with_config(@config)
368
+ elsif command[1].casecmp('failover').zero?
369
+ raise ::RedisClient::Cluster::OrchestrationCommandNotSupported.from_command(['cluster', command[1]]).with_config(@config)
370
+ elsif command[1].casecmp('forget').zero?
371
+ raise ::RedisClient::Cluster::OrchestrationCommandNotSupported.from_command(['cluster', command[1]]).with_config(@config)
372
+ elsif command[1].casecmp('meet').zero?
373
+ raise ::RedisClient::Cluster::OrchestrationCommandNotSupported.from_command(['cluster', command[1]]).with_config(@config)
374
+ elsif command[1].casecmp('replicate').zero?
375
+ raise ::RedisClient::Cluster::OrchestrationCommandNotSupported.from_command(['cluster', command[1]]).with_config(@config)
376
+ elsif command[1].casecmp('reset').zero?
377
+ raise ::RedisClient::Cluster::OrchestrationCommandNotSupported.from_command(['cluster', command[1]]).with_config(@config)
378
+ elsif command[1].casecmp('set-config-epoch').zero?
379
+ raise ::RedisClient::Cluster::OrchestrationCommandNotSupported.from_command(['cluster', command[1]]).with_config(@config)
380
+ elsif command[1].casecmp('setslot').zero?
381
+ raise ::RedisClient::Cluster::OrchestrationCommandNotSupported.from_command(['cluster', command[1]]).with_config(@config)
382
+ elsif command[1].casecmp('saveconfig').zero?
383
+ @node.call_all(method, command, args).first.then(&TSF.call(block))
384
+ elsif command[1].casecmp('getkeysinslot').zero?
315
385
  raise ArgumentError, command.join(' ') if command.size != 4
316
386
 
317
387
  handle_node_reload_error do
318
388
  node_key = @node.find_node_key_of_replica(command[2])
319
389
  @node.find_by(node_key).public_send(method, *args, command, &block)
320
390
  end
321
- else assign_node(command).public_send(method, *args, command, &block)
391
+ else
392
+ assign_node(command).public_send(method, *args, command, &block)
322
393
  end
323
394
  end
324
395
 
325
- def send_script_command(method, command, args, &block) # rubocop:disable Metrics/AbcSize
326
- case ::RedisClient::Cluster::NormalizedCmdName.instance.get_by_subcommand(command)
327
- when 'debug', 'kill'
396
+ def send_script_command(method, command, args, &block) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
397
+ if command[1].casecmp('debug').zero?
398
+ @node.call_all(method, command, args).first.then(&TSF.call(block))
399
+ elsif command[1].casecmp('kill').zero?
328
400
  @node.call_all(method, command, args).first.then(&TSF.call(block))
329
- when 'flush', 'load'
401
+ elsif command[1].casecmp('flush').zero?
402
+ @node.call_primaries(method, command, args).first.then(&TSF.call(block))
403
+ elsif command[1].casecmp('load').zero?
330
404
  @node.call_primaries(method, command, args).first.then(&TSF.call(block))
331
- when 'exists'
405
+ elsif command[1].casecmp('exists').zero?
332
406
  @node.call_all(method, command, args).transpose.map { |arr| arr.any?(&:zero?) ? 0 : 1 }.then(&TSF.call(block))
333
- else assign_node(command).public_send(method, *args, command, &block)
407
+ else
408
+ assign_node(command).public_send(method, *args, command, &block)
334
409
  end
335
410
  end
336
411
 
337
412
  def send_pubsub_command(method, command, args, &block) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
338
- case ::RedisClient::Cluster::NormalizedCmdName.instance.get_by_subcommand(command)
339
- when 'channels'
413
+ if command[1].casecmp('channels').zero?
340
414
  @node.call_all(method, command, args).flatten.uniq.sort_by(&:to_s).then(&TSF.call(block))
341
- when 'shardchannels'
415
+ elsif command[1].casecmp('shardchannels').zero?
342
416
  @node.call_replicas(method, command, args).flatten.uniq.sort_by(&:to_s).then(&TSF.call(block))
343
- when 'numpat'
417
+ elsif command[1].casecmp('numpat').zero?
344
418
  @node.call_all(method, command, args).select { |e| e.is_a?(Integer) }.sum.then(&TSF.call(block))
345
- when 'numsub'
419
+ elsif command[1].casecmp('numsub').zero?
346
420
  @node.call_all(method, command, args).reject(&:empty?).map { |e| Hash[*e] }
347
421
  .reduce({}) { |a, e| a.merge(e) { |_, v1, v2| v1 + v2 } }.then(&TSF.call(block))
348
- when 'shardnumsub'
422
+ elsif command[1].casecmp('shardnumsub').zero?
349
423
  @node.call_replicas(method, command, args).reject(&:empty?).map { |e| Hash[*e] }
350
424
  .reduce({}) { |a, e| a.merge(e) { |_, v1, v2| v1 + v2 } }.then(&TSF.call(block))
351
- else assign_node(command).public_send(method, *args, command, &block)
425
+ else
426
+ assign_node(command).public_send(method, *args, command, &block)
352
427
  end
353
428
  end
354
429
 
@@ -369,17 +444,17 @@ class RedisClient
369
444
 
370
445
  def send_multiple_keys_command(cmd, method, command, args, &block) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
371
446
  # This implementation is prioritized performance rather than readability or so.
372
- case cmd
373
- when 'mget'
447
+ if cmd.casecmp('mget').zero?
374
448
  single_key_cmd = 'get'
375
449
  keys_step = 1
376
- when 'mset'
450
+ elsif cmd.casecmp('mset').zero?
377
451
  single_key_cmd = 'set'
378
452
  keys_step = 2
379
- when 'del'
453
+ elsif cmd.casecmp('del').zero?
380
454
  single_key_cmd = 'del'
381
455
  keys_step = 1
382
- else raise NotImplementedError, cmd
456
+ else
457
+ raise NotImplementedError, cmd
383
458
  end
384
459
 
385
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])
@@ -402,10 +477,12 @@ class RedisClient
402
477
  end
403
478
 
404
479
  replies = pipeline.execute
405
- result = case cmd
406
- when 'mset' then replies.first
407
- when 'del' then replies.sum
408
- else replies
480
+ result = if cmd.casecmp('mset').zero?
481
+ replies.first
482
+ elsif cmd.casecmp('del').zero?
483
+ replies.sum
484
+ else
485
+ replies
409
486
  end
410
487
  block_given? ? yield(result) : result
411
488
  end
@@ -93,24 +93,24 @@ class RedisClient
93
93
  end
94
94
 
95
95
  def prepare_tx
96
- @pipeline.call('MULTI')
96
+ @pipeline.call('multi')
97
97
  @pending_commands.each(&:call)
98
98
  @pending_commands.clear
99
99
  end
100
100
 
101
101
  def commit
102
- @pipeline.call('EXEC')
102
+ @pipeline.call('exec')
103
103
  settle
104
104
  end
105
105
 
106
106
  def cancel
107
- @pipeline.call('DISCARD')
107
+ @pipeline.call('discard')
108
108
  settle
109
109
  end
110
110
 
111
111
  def settle
112
112
  # If we needed ASKING on the watch, we need ASKING on the multi as well.
113
- @node.call('ASKING') if @asking
113
+ @node.call('asking') if @asking
114
114
  # Don't handle redirections at this level if we're in a watch (the watcher handles redirections
115
115
  # at the whole-transaction level.)
116
116
  send_transaction(@node, redirect: !!@watching_slot ? 0 : MAX_REDIRECTION)
@@ -189,7 +189,7 @@ class RedisClient
189
189
  end
190
190
 
191
191
  def try_asking(node)
192
- node.call('ASKING') == 'OK'
192
+ node.call('asking') == 'OK'
193
193
  rescue StandardError
194
194
  false
195
195
  end
@@ -152,8 +152,9 @@ class RedisClient
152
152
  end
153
153
 
154
154
  def method_missing(name, *args, **kwargs, &block)
155
- if router.command_exists?(name)
156
- args.unshift(name)
155
+ cmd = name.respond_to?(:name) ? name.name : name.to_s
156
+ if router.command_exists?(cmd)
157
+ args.unshift(cmd)
157
158
  command = @command_builder.generate(args, kwargs)
158
159
  return router.send_command(:call_v, command, &block)
159
160
  end
@@ -19,7 +19,7 @@ class RedisClient
19
19
  VALID_NODES_KEYS = %i[ssl username password host port db].freeze
20
20
  MERGE_CONFIG_KEYS = %i[ssl username password].freeze
21
21
  IGNORE_GENERIC_CONFIG_KEYS = %i[url host port path].freeze
22
- MAX_WORKERS = Integer(ENV.fetch('REDIS_CLIENT_MAX_THREADS', 5))
22
+ MAX_WORKERS = Integer(ENV.fetch('REDIS_CLIENT_MAX_THREADS', -1)) # for backward compatibility
23
23
  # It's used with slow queries of fetching meta data like CLUSTER NODES, COMMAND and so on.
24
24
  SLOW_COMMAND_TIMEOUT = Float(ENV.fetch('REDIS_CLIENT_SLOW_COMMAND_TIMEOUT', -1))
25
25
  # It affects to strike a balance between load and stability in initialization or changed states.
@@ -110,12 +110,16 @@ class RedisClient
110
110
  private
111
111
 
112
112
  def merge_concurrency_option(option)
113
- case option
114
- when Hash
115
- option = option.transform_keys(&:to_sym)
116
- { size: MAX_WORKERS }.merge(option)
117
- else { size: MAX_WORKERS }
113
+ opts = {}
114
+
115
+ if MAX_WORKERS.positive?
116
+ opts[:model] = :on_demand
117
+ opts[:size] = MAX_WORKERS
118
118
  end
119
+
120
+ opts.merge!(option.transform_keys(&:to_sym)) if option.is_a?(Hash)
121
+ opts[:model] = :none if opts.empty?
122
+ opts.freeze
119
123
  end
120
124
 
121
125
  def build_node_configs(addrs)
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.12.1
4
+ version: 0.13.0
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-18 00:00:00.000000000 Z
11
+ date: 2024-11-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: redis-client
@@ -49,7 +49,6 @@ files:
49
49
  - lib/redis_client/cluster/node/random_replica_or_primary.rb
50
50
  - lib/redis_client/cluster/node_key.rb
51
51
  - lib/redis_client/cluster/noop_command_builder.rb
52
- - lib/redis_client/cluster/normalized_cmd_name.rb
53
52
  - lib/redis_client/cluster/optimistic_locking.rb
54
53
  - lib/redis_client/cluster/pipeline.rb
55
54
  - lib/redis_client/cluster/pub_sub.rb
@@ -1,71 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'singleton'
4
-
5
- class RedisClient
6
- class Cluster
7
- class NormalizedCmdName
8
- include Singleton
9
-
10
- EMPTY_STRING = ''
11
-
12
- private_constant :EMPTY_STRING
13
-
14
- def initialize
15
- @cache = {}
16
- @mutex = Mutex.new
17
- end
18
-
19
- def get_by_command(command)
20
- get(command, index: 0)
21
- end
22
-
23
- def get_by_subcommand(command)
24
- get(command, index: 1)
25
- end
26
-
27
- def get_by_name(name)
28
- get(name, index: 0)
29
- end
30
-
31
- def clear
32
- @mutex.synchronize { @cache.clear }
33
- true
34
- end
35
-
36
- private
37
-
38
- def get(command, index:)
39
- name = extract_name(command, index: index)
40
- return EMPTY_STRING if name.nil? || name.empty?
41
-
42
- normalize(name)
43
- end
44
-
45
- def extract_name(command, index:)
46
- case command
47
- when String, Symbol then index.zero? ? command : nil
48
- when Array then extract_name_from_array(command, index: index)
49
- end
50
- end
51
-
52
- def extract_name_from_array(command, index:)
53
- return if command.size - 1 < index
54
-
55
- case e = command[index]
56
- when String, Symbol then e
57
- when Array then e[index]
58
- end
59
- end
60
-
61
- def normalize(name)
62
- return @cache[name] || name.to_s.downcase if @cache.key?(name)
63
- return name.to_s.downcase if @mutex.locked?
64
-
65
- str = name.to_s.downcase
66
- @mutex.synchronize { @cache[name] = str }
67
- str
68
- end
69
- end
70
- end
71
- end