redis-cluster-client 0.11.6 → 0.12.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: 9e31ae2691d5dac09f55bffb25f1a5af9f0f300d9b71246f998d7a18ad6d6d97
4
- data.tar.gz: 7331c0ea5c04d95bb9325ad235edbede9fa872dacdcc1df2745214241600f7fa
3
+ metadata.gz: 2465b865cd79c78cca3a1e7e3950989cce94f6675b0497763e03e261d3fa9c6f
4
+ data.tar.gz: df607279a58171aca75835b767f3bf5965256db0fcb7b540bfcb3a8f302eb638
5
5
  SHA512:
6
- metadata.gz: f8752f06007254fd8c73716f3a93853062e8bb61d9381cdf66e56f705a4f7c391e0f2685ee2b75d97d2bd25aac815ae17997e0982b0774c78324b04637af1574
7
- data.tar.gz: f995c63ae5e11ff31a0ce4feaef8c421620244c192d32ae3522a71ca62956b362962c15a87daa8c76c50018368c2851d0c99c83b6a061fa1ef7024da3df4e6fd
6
+ metadata.gz: bfb9031660b642bb14768dc8edc66fb15073be9ca23ddc061d279a351c497a83ff8a3aad21903e2d0a37aa6824da35761188ba4dc1fe8ae0182b54584edaa5e5
7
+ data.tar.gz: 9fab63d0595bfb7dac85a07c255a41573ab8d44598605919fd4182e99cf3da585bf931abe63da9e2f470815c6ef0d34ab24a5b7f2de346ba29870fb894c556ee
@@ -25,7 +25,7 @@ class RedisClient
25
25
  )
26
26
 
27
27
  class << self
28
- def load(nodes, slow_command_timeout: -1)
28
+ def load(nodes, slow_command_timeout: -1) # rubocop:disable Metrics/AbcSize
29
29
  cmd = errors = nil
30
30
 
31
31
  nodes&.each do |node|
@@ -43,7 +43,7 @@ class RedisClient
43
43
 
44
44
  return cmd unless cmd.nil?
45
45
 
46
- raise ::RedisClient::Cluster::InitialSetupError, errors
46
+ raise ::RedisClient::Cluster::InitialSetupError.from_errors(errors)
47
47
  end
48
48
 
49
49
  private
@@ -4,51 +4,60 @@ require 'redis_client'
4
4
 
5
5
  class RedisClient
6
6
  class Cluster
7
+ class Error < ::RedisClient::Error
8
+ def with_config(config)
9
+ @config = config
10
+ self
11
+ end
12
+ end
13
+
7
14
  ERR_ARG_NORMALIZATION = ->(arg) { Array[arg].flatten.reject { |e| e.nil? || (e.respond_to?(:empty?) && e.empty?) } }
8
15
 
9
16
  private_constant :ERR_ARG_NORMALIZATION
10
17
 
11
- class InitialSetupError < ::RedisClient::Error
12
- def initialize(errors)
18
+ class InitialSetupError < Error
19
+ def self.from_errors(errors)
13
20
  msg = ERR_ARG_NORMALIZATION.call(errors).map(&:message).uniq.join(',')
14
- super("Redis client could not fetch cluster information: #{msg}")
21
+ new("Redis client could not fetch cluster information: #{msg}")
15
22
  end
16
23
  end
17
24
 
18
- class OrchestrationCommandNotSupported < ::RedisClient::Error
19
- def initialize(command)
25
+ class OrchestrationCommandNotSupported < Error
26
+ def self.from_command(command)
20
27
  str = ERR_ARG_NORMALIZATION.call(command).map(&:to_s).join(' ').upcase
21
28
  msg = "#{str} command should be used with care " \
22
29
  'only by applications orchestrating Redis Cluster, like redis-cli, ' \
23
30
  'and the command if used out of the right context can leave the cluster ' \
24
31
  'in a wrong state or cause data loss.'
25
- super(msg)
32
+ new(msg)
26
33
  end
27
34
  end
28
35
 
29
- class ErrorCollection < ::RedisClient::Error
36
+ class ErrorCollection < Error
30
37
  attr_reader :errors
31
38
 
32
- def initialize(errors)
33
- @errors = {}
39
+ def self.with_errors(errors)
34
40
  if !errors.is_a?(Hash) || errors.empty?
35
- super(errors.to_s)
36
- return
41
+ new(errors.to_s).with_errors({})
42
+ else
43
+ messages = errors.map { |node_key, error| "#{node_key}: (#{error.class}) #{error.message}" }
44
+ new(messages.join(', ')).with_errors(errors)
37
45
  end
46
+ end
38
47
 
39
- @errors = errors
40
- messages = @errors.map { |node_key, error| "#{node_key}: (#{error.class}) #{error.message}" }
41
- super(messages.join(', '))
48
+ def with_errors(errors)
49
+ @errors = errors if @errors.nil?
50
+ self
42
51
  end
43
52
  end
44
53
 
45
- class AmbiguousNodeError < ::RedisClient::Error
46
- def initialize(command)
47
- super("Cluster client doesn't know which node the #{command} command should be sent to.")
54
+ class AmbiguousNodeError < Error
55
+ def self.from_command(command)
56
+ new("Cluster client doesn't know which node the #{command} command should be sent to.")
48
57
  end
49
58
  end
50
59
 
51
- class NodeMightBeDown < ::RedisClient::Error
60
+ class NodeMightBeDown < Error
52
61
  def initialize(_ = '')
53
62
  super(
54
63
  'The client is trying to fetch the latest cluster state ' \
@@ -28,7 +28,7 @@ class RedisClient
28
28
  private_constant :USE_CHAR_ARRAY_SLOT, :SLOT_SIZE, :MIN_SLOT, :MAX_SLOT,
29
29
  :DEAD_FLAGS, :ROLE_FLAGS, :EMPTY_ARRAY, :EMPTY_HASH
30
30
 
31
- ReloadNeeded = Class.new(::RedisClient::Error)
31
+ ReloadNeeded = Class.new(::RedisClient::Cluster::Error)
32
32
 
33
33
  Info = Struct.new(
34
34
  'RedisClusterNode',
@@ -148,7 +148,7 @@ class RedisClient
148
148
 
149
149
  raise ReloadNeeded if errors.values.any?(::RedisClient::ConnectionError)
150
150
 
151
- raise ::RedisClient::Cluster::ErrorCollection, errors
151
+ raise ::RedisClient::Cluster::ErrorCollection.with_errors(errors)
152
152
  end
153
153
 
154
154
  def clients_for_scanning(seed: nil)
@@ -267,7 +267,7 @@ class RedisClient
267
267
  result_values, errors = call_multiple_nodes(clients, method, command, args, &block)
268
268
  return result_values if errors.nil? || errors.empty?
269
269
 
270
- raise ::RedisClient::Cluster::ErrorCollection, errors
270
+ raise ::RedisClient::Cluster::ErrorCollection.with_errors(errors)
271
271
  end
272
272
 
273
273
  def try_map(clients, &block) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity
@@ -334,7 +334,7 @@ class RedisClient
334
334
 
335
335
  work_group.close
336
336
 
337
- raise ::RedisClient::Cluster::InitialSetupError, errors if node_info_list.nil?
337
+ raise ::RedisClient::Cluster::InitialSetupError.from_errors(errors) if node_info_list.nil?
338
338
 
339
339
  grouped = node_info_list.compact.group_by do |info_list|
340
340
  info_list.sort_by!(&:id)
@@ -108,13 +108,13 @@ class RedisClient
108
108
  end
109
109
  end
110
110
 
111
- ReplySizeError = Class.new(::RedisClient::Error)
111
+ ReplySizeError = Class.new(::RedisClient::Cluster::Error)
112
112
 
113
- class StaleClusterState < ::RedisClient::Error
113
+ class StaleClusterState < ::RedisClient::Cluster::Error
114
114
  attr_accessor :replies, :first_exception
115
115
  end
116
116
 
117
- class RedirectionNeeded < ::RedisClient::Error
117
+ class RedirectionNeeded < ::RedisClient::Cluster::Error
118
118
  attr_accessor :replies, :indices, :first_exception
119
119
  end
120
120
 
@@ -204,7 +204,7 @@ class RedisClient
204
204
 
205
205
  work_group.close
206
206
  @router.renew_cluster_state if cluster_state_errors
207
- raise ::RedisClient::Cluster::ErrorCollection, errors unless errors.nil?
207
+ raise ::RedisClient::Cluster::ErrorCollection.with_errors(errors).with_config(@router.config) unless errors.nil?
208
208
 
209
209
  required_redirections&.each do |node_key, v|
210
210
  raise v.first_exception if v.first_exception
@@ -21,10 +21,10 @@ class RedisClient
21
21
 
22
22
  private_constant :ZERO_CURSOR_FOR_SCAN, :TSF
23
23
 
24
+ attr_reader :config
25
+
24
26
  def initialize(config, concurrent_worker, pool: nil, **kwargs)
25
- @config = config.dup
26
- @original_config = config.dup if config.connect_with_original_config
27
- @connect_with_original_config = config.connect_with_original_config
27
+ @config = config
28
28
  @concurrent_worker = concurrent_worker
29
29
  @pool = pool
30
30
  @client_kwargs = kwargs
@@ -32,6 +32,9 @@ class RedisClient
32
32
  @node.reload!
33
33
  @command = ::RedisClient::Cluster::Command.load(@node.replica_clients.shuffle, slow_command_timeout: config.slow_command_timeout)
34
34
  @command_builder = @config.command_builder
35
+ rescue ::RedisClient::Cluster::InitialSetupError => e
36
+ e.with_config(config)
37
+ raise
35
38
  end
36
39
 
37
40
  def send_command(method, command, *args, &block) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
@@ -58,9 +61,9 @@ class RedisClient
58
61
  when 'flushall', 'flushdb'
59
62
  @node.call_primaries(method, command, args).first.then(&TSF.call(block))
60
63
  when 'readonly', 'readwrite', 'shutdown'
61
- raise ::RedisClient::Cluster::OrchestrationCommandNotSupported, cmd
64
+ raise ::RedisClient::Cluster::OrchestrationCommandNotSupported.from_command(cmd).with_config(@config)
62
65
  when 'discard', 'exec', 'multi', 'unwatch'
63
- raise ::RedisClient::Cluster::AmbiguousNodeError, cmd
66
+ raise ::RedisClient::Cluster::AmbiguousNodeError.from_command(cmd).with_config(@config)
64
67
  else
65
68
  node = assign_node(command)
66
69
  try_send(node, method, command, args, &block)
@@ -69,7 +72,7 @@ class RedisClient
69
72
  raise
70
73
  rescue ::RedisClient::Cluster::Node::ReloadNeeded
71
74
  renew_cluster_state
72
- raise ::RedisClient::Cluster::NodeMightBeDown
75
+ raise ::RedisClient::Cluster::NodeMightBeDown.new.with_config(@config)
73
76
  rescue ::RedisClient::ConnectionError
74
77
  renew_cluster_state
75
78
  raise
@@ -77,6 +80,7 @@ class RedisClient
77
80
  renew_cluster_state if e.message.start_with?('CLUSTERDOWN')
78
81
  raise
79
82
  rescue ::RedisClient::Cluster::ErrorCollection => e
83
+ e.with_config(@config)
80
84
  raise if e.errors.any?(::RedisClient::CircuitBreaker::OpenCircuitError)
81
85
 
82
86
  renew_cluster_state if e.errors.values.any? do |err|
@@ -189,7 +193,7 @@ class RedisClient
189
193
  node_key = primary ? @node.find_node_key_of_primary(slot) : @node.find_node_key_of_replica(slot)
190
194
  if node_key.nil?
191
195
  renew_cluster_state
192
- raise ::RedisClient::Cluster::NodeMightBeDown
196
+ raise ::RedisClient::Cluster::NodeMightBeDown.new.with_config(@config)
193
197
  end
194
198
  node_key
195
199
  else
@@ -303,7 +307,7 @@ class RedisClient
303
307
  case subcommand = ::RedisClient::Cluster::NormalizedCmdName.instance.get_by_subcommand(command)
304
308
  when 'addslots', 'delslots', 'failover', 'forget', 'meet', 'replicate',
305
309
  'reset', 'set-config-epoch', 'setslot'
306
- raise ::RedisClient::Cluster::OrchestrationCommandNotSupported, ['cluster', subcommand]
310
+ raise ::RedisClient::Cluster::OrchestrationCommandNotSupported.from_command(['cluster', subcommand]).with_config(@config)
307
311
  when 'saveconfig' then @node.call_all(method, command, args).first.then(&TSF.call(block))
308
312
  when 'getkeysinslot'
309
313
  raise ArgumentError, command.join(' ') if command.size != 4
@@ -347,7 +351,10 @@ class RedisClient
347
351
  end
348
352
 
349
353
  def send_watch_command(command)
350
- raise ::RedisClient::Cluster::Transaction::ConsistencyError, 'A block required. And you need to use the block argument as a client for the transaction.' unless block_given?
354
+ unless block_given?
355
+ msg = 'A block required. And you need to use the block argument as a client for the transaction.'
356
+ raise ::RedisClient::Cluster::Transaction::ConsistencyError.new(msg).with_config(@config)
357
+ end
351
358
 
352
359
  ::RedisClient::Cluster::OptimisticLocking.new(self).watch(command[1..]) do |c, slot, asking|
353
360
  transaction = ::RedisClient::Cluster::Transaction.new(
@@ -401,7 +408,7 @@ class RedisClient
401
408
  def handle_node_reload_error(retry_count: 1)
402
409
  yield
403
410
  rescue ::RedisClient::Cluster::Node::ReloadNeeded
404
- raise ::RedisClient::Cluster::NodeMightBeDown if retry_count <= 0
411
+ raise ::RedisClient::Cluster::NodeMightBeDown.new.with_config(@config) if retry_count <= 0
405
412
 
406
413
  retry_count -= 1
407
414
  renew_cluster_state
@@ -1,12 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'redis_client'
4
+ require 'redis_client/cluster/errors'
4
5
  require 'redis_client/cluster/pipeline'
5
6
 
6
7
  class RedisClient
7
8
  class Cluster
8
9
  class Transaction
9
- ConsistencyError = Class.new(::RedisClient::Error)
10
+ ConsistencyError = Class.new(::RedisClient::Cluster::Error)
10
11
 
11
12
  MAX_REDIRECTION = 2
12
13
  EMPTY_ARRAY = [].freeze
@@ -67,7 +68,7 @@ class RedisClient
67
68
  @pending_commands.each(&:call)
68
69
 
69
70
  return EMPTY_ARRAY if @pipeline._empty?
70
- raise ConsistencyError, "couldn't determine the node: #{@pipeline._commands}" if @node.nil?
71
+ raise ConsistencyError.new("couldn't determine the node: #{@pipeline._commands}").with_config(@router.config) if @node.nil?
71
72
 
72
73
  commit
73
74
  end
@@ -163,7 +164,7 @@ class RedisClient
163
164
 
164
165
  def handle_command_error!(err, redirect:) # rubocop:disable Metrics/AbcSize
165
166
  if err.message.start_with?('CROSSSLOT')
166
- raise ConsistencyError, "#{err.message}: #{err.command}"
167
+ raise ConsistencyError.new("#{err.message}: #{err.command}").with_config(@router.config)
167
168
  elsif err.message.start_with?('MOVED')
168
169
  node = @router.assign_redirection_node(err.message)
169
170
  send_transaction(node, redirect: redirect - 1)
@@ -183,7 +184,7 @@ class RedisClient
183
184
  return if slots.size == 1 && @watching_slot.nil?
184
185
  return if slots.size == 1 && @watching_slot == slots.first
185
186
 
186
- raise(ConsistencyError, "the transaction should be executed to a slot in a node: #{commands}")
187
+ raise ConsistencyError.new("the transaction should be executed to a slot in a node: #{commands}").with_config(@router.config)
187
188
  end
188
189
 
189
190
  def try_asking(node)
@@ -3,6 +3,7 @@
3
3
  require 'uri'
4
4
  require 'redis_client'
5
5
  require 'redis_client/cluster'
6
+ require 'redis_client/cluster/errors'
6
7
  require 'redis_client/cluster/node_key'
7
8
  require 'redis_client/command_builder'
8
9
 
@@ -27,10 +28,10 @@ class RedisClient
27
28
  :VALID_SCHEMES, :VALID_NODES_KEYS, :MERGE_CONFIG_KEYS, :IGNORE_GENERIC_CONFIG_KEYS,
28
29
  :MAX_WORKERS, :SLOW_COMMAND_TIMEOUT, :MAX_STARTUP_SAMPLE
29
30
 
30
- InvalidClientConfigError = Class.new(::RedisClient::Error)
31
+ InvalidClientConfigError = Class.new(::RedisClient::Cluster::Error)
31
32
 
32
33
  attr_reader :command_builder, :client_config, :replica_affinity, :slow_command_timeout,
33
- :connect_with_original_config, :startup_nodes, :max_startup_sample
34
+ :connect_with_original_config, :startup_nodes, :max_startup_sample, :id
34
35
 
35
36
  def initialize( # rubocop:disable Metrics/ParameterLists
36
37
  nodes: DEFAULT_NODES,
@@ -59,6 +60,7 @@ class RedisClient
59
60
  @client_implementation = client_implementation
60
61
  @slow_command_timeout = slow_command_timeout
61
62
  @max_startup_sample = max_startup_sample
63
+ @id = client_config[:id]
62
64
  end
63
65
 
64
66
  def inspect
@@ -92,6 +94,18 @@ class RedisClient
92
94
  augment_client_config(config)
93
95
  end
94
96
 
97
+ def resolved?
98
+ true
99
+ end
100
+
101
+ def sentinel?
102
+ false
103
+ end
104
+
105
+ def server_url
106
+ nil
107
+ end
108
+
95
109
  private
96
110
 
97
111
  def merge_concurrency_option(option)
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.11.6
4
+ version: 0.12.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-10-17 00:00:00.000000000 Z
11
+ date: 2024-11-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: redis-client
@@ -77,7 +77,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
77
77
  - !ruby/object:Gem::Version
78
78
  version: '0'
79
79
  requirements: []
80
- rubygems_version: 3.5.16
80
+ rubygems_version: 3.5.22
81
81
  signing_key:
82
82
  specification_version: 4
83
83
  summary: A Redis cluster client for Ruby