redis-cluster-client 0.3.15 → 0.4.0

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: c0492c31a84cc5a5982973891f9c305d67d6121f03fb12fef76b7045a8960421
4
- data.tar.gz: e2531e6b21591974103c794cea27fa7383fb3c7aef27ab08850d7468bc6555c0
3
+ metadata.gz: 780e5f1653b6633dbd4bb1bdc4f4da174d70176a5287bf6156ad5784f0612cf1
4
+ data.tar.gz: ca1681357111afdc82bdf32a18985963dba85f89fd49961ac62c56075daa0cc8
5
5
  SHA512:
6
- metadata.gz: 472a05c3122f7847df1fa316ac1e20722abb5c34a299daf2dee71cc71a3cf74437100f97c9ed2105a105b517bdf01066a8702296182126a232d5b7cc463caac0
7
- data.tar.gz: 9c5bfa33629d5ec005e20c8672748967796a150a190a7aa9de46f11225731b0cec6ce02b6e00b7b94ba1703d5bd103e584d538ccf5219a10c713fbc206f07598
6
+ metadata.gz: 4111e4b1305d955d3eb56df912debd0cfbbd388bae1d841cf1b9274a21d2e121fb511f1630d85f6c503d53ebf514503303b94402d3416517886b93f615584a22
7
+ data.tar.gz: 1f6e2be52bcd834482d17bebfeead2b8d1346c954d03b61251dd973ab19f27275e53571d73ba6425dceedc34dc07cd5e6dbb98fa46de00281b6d00eeb1c87f08
@@ -43,7 +43,7 @@ class RedisClient
43
43
  clients.each_slice(::RedisClient::Cluster::Node::MAX_THREADS).each_with_object({}) do |chuncked_clients, acc|
44
44
  threads = chuncked_clients.map do |k, v|
45
45
  Thread.new(k, v) do |node_key, client|
46
- Thread.current.thread_variable_set(:node_key, node_key)
46
+ Thread.current[:node_key] = node_key
47
47
 
48
48
  min = DUMMY_LATENCY_NSEC
49
49
  MEASURE_ATTEMPT_COUNT.times do
@@ -53,15 +53,15 @@ class RedisClient
53
53
  min = duration if duration < min
54
54
  end
55
55
 
56
- Thread.current.thread_variable_set(:latency, min)
56
+ Thread.current[:latency] = min
57
57
  rescue StandardError
58
- Thread.current.thread_variable_set(:latency, DUMMY_LATENCY_NSEC)
58
+ Thread.current[:latency] = DUMMY_LATENCY_NSEC
59
59
  end
60
60
  end
61
61
 
62
62
  threads.each do |t|
63
63
  t.join
64
- acc[t.thread_variable_get(:node_key)] = t.thread_variable_get(:latency)
64
+ acc[t[:node_key]] = t[:latency]
65
65
  end
66
66
  end
67
67
  end
@@ -18,7 +18,6 @@ class RedisClient
18
18
  MAX_STARTUP_SAMPLE = 37
19
19
  MAX_THREADS = Integer(ENV.fetch('REDIS_CLIENT_MAX_THREADS', 5))
20
20
  IGNORE_GENERIC_CONFIG_KEYS = %i[url host port path].freeze
21
- SLOT_OPTIMIZATION_STRING = '0' * SLOT_SIZE
22
21
 
23
22
  ReloadNeeded = Class.new(::RedisClient::Error)
24
23
 
@@ -37,27 +36,36 @@ class RedisClient
37
36
  end
38
37
  end
39
38
 
40
- Slot = Struct.new('StringArray', :string, :elements, keyword_init: true) do
39
+ class CharArray
40
+ BASE = ''
41
+ PADDING = '0'
42
+
43
+ def initialize(size, elements)
44
+ @elements = elements
45
+ @string = String.new(BASE, encoding: Encoding::BINARY, capacity: size)
46
+ size.times { @string << PADDING }
47
+ end
48
+
41
49
  def [](index)
42
50
  raise IndexError if index < 0
43
- return if index >= string.bytesize
51
+ return if index >= @string.bytesize
44
52
 
45
- elements[string.getbyte(index)]
53
+ @elements[@string.getbyte(index)]
46
54
  end
47
55
 
48
56
  def []=(index, element)
49
57
  raise IndexError if index < 0
50
- return if index >= string.bytesize
58
+ return if index >= @string.bytesize
51
59
 
52
- pos = elements.find_index(element) # O(N)
60
+ pos = @elements.find_index(element) # O(N)
53
61
  if pos.nil?
54
- raise(RangeError, 'full of elements') if elements.size >= 256
62
+ raise(RangeError, 'full of elements') if @elements.size >= 256
55
63
 
56
- pos = elements.size
57
- elements << element
64
+ pos = @elements.size
65
+ @elements << element
58
66
  end
59
67
 
60
- string.setbyte(index, pos)
68
+ @string.setbyte(index, pos)
61
69
  end
62
70
  end
63
71
 
@@ -85,11 +93,11 @@ class RedisClient
85
93
  startup_nodes.each_slice(MAX_THREADS).with_index do |chuncked_startup_nodes, chuncked_idx|
86
94
  threads = chuncked_startup_nodes.each_with_index.map do |raw_client, idx|
87
95
  Thread.new(raw_client, (MAX_THREADS * chuncked_idx) + idx) do |cli, i|
88
- Thread.current.thread_variable_set(:index, i)
96
+ Thread.current[:index] = i
89
97
  reply = cli.call('CLUSTER', 'NODES')
90
- Thread.current.thread_variable_set(:info, parse_cluster_node_reply(reply))
98
+ Thread.current[:info] = parse_cluster_node_reply(reply)
91
99
  rescue StandardError => e
92
- Thread.current.thread_variable_set(:error, e)
100
+ Thread.current[:error] = e
93
101
  ensure
94
102
  cli&.close
95
103
  end
@@ -97,12 +105,12 @@ class RedisClient
97
105
 
98
106
  threads.each do |t|
99
107
  t.join
100
- if t.thread_variable?(:info)
108
+ if t.key?(:info)
101
109
  node_info_list ||= Array.new(startup_size)
102
- node_info_list[t.thread_variable_get(:index)] = t.thread_variable_get(:info)
103
- elsif t.thread_variable?(:error)
110
+ node_info_list[t[:index]] = t[:info]
111
+ elsif t.key?(:error)
104
112
  errors ||= Array.new(startup_size)
105
- errors[t.thread_variable_get(:index)] = t.thread_variable_get(:error)
113
+ errors[t[:index]] = t[:error]
106
114
  end
107
115
  end
108
116
  end
@@ -268,10 +276,8 @@ class RedisClient
268
276
  def make_array_for_slot_node_mappings(node_info_list)
269
277
  return Array.new(SLOT_SIZE) if node_info_list.count(&:primary?) > 256
270
278
 
271
- ::RedisClient::Cluster::Node::Slot.new(
272
- string: String.new(SLOT_OPTIMIZATION_STRING, encoding: Encoding::BINARY, capacity: SLOT_SIZE),
273
- elements: node_info_list.select(&:primary?).map(&:node_key)
274
- )
279
+ primary_node_keys = node_info_list.select(&:primary?).map(&:node_key)
280
+ ::RedisClient::Cluster::Node::CharArray.new(SLOT_SIZE, primary_node_keys)
275
281
  end
276
282
 
277
283
  def build_replication_mappings(node_info_list) # rubocop:disable Metrics/AbcSize
@@ -303,22 +309,22 @@ class RedisClient
303
309
  clients.each_slice(MAX_THREADS) do |chuncked_clients|
304
310
  threads = chuncked_clients.map do |k, v|
305
311
  Thread.new(k, v) do |node_key, client|
306
- Thread.current.thread_variable_set(:node_key, node_key)
312
+ Thread.current[:node_key] = node_key
307
313
  reply = yield(node_key, client)
308
- Thread.current.thread_variable_set(:result, reply)
314
+ Thread.current[:result] = reply
309
315
  rescue StandardError => e
310
- Thread.current.thread_variable_set(:error, e)
316
+ Thread.current[:error] = e
311
317
  end
312
318
  end
313
319
 
314
320
  threads.each do |t|
315
321
  t.join
316
- if t.thread_variable?(:result)
322
+ if t.key?(:result)
317
323
  results ||= {}
318
- results[t.thread_variable_get(:node_key)] = t.thread_variable_get(:result)
319
- elsif t.thread_variable?(:error)
324
+ results[t[:node_key]] = t[:result]
325
+ elsif t.key?(:error)
320
326
  errors ||= {}
321
- errors[t.thread_variable_get(:node_key)] = t.thread_variable_get(:error)
327
+ errors[t[:node_key]] = t[:error]
322
328
  end
323
329
  end
324
330
  end
@@ -150,34 +150,34 @@ class RedisClient
150
150
  @pipelines&.each_slice(MAX_THREADS) do |chuncked_pipelines|
151
151
  threads = chuncked_pipelines.map do |node_key, pipeline|
152
152
  Thread.new(node_key, pipeline) do |nk, pl|
153
- Thread.current.thread_variable_set(:node_key, nk)
153
+ Thread.current[:node_key] = nk
154
154
  replies = do_pipelining(@router.find_node(nk), pl)
155
155
  raise ReplySizeError, "commands: #{pl._size}, replies: #{replies.size}" if pl._size != replies.size
156
156
 
157
- Thread.current.thread_variable_set(:replies, replies)
157
+ Thread.current[:replies] = replies
158
158
  rescue ::RedisClient::Cluster::Pipeline::RedirectionNeeded => e
159
- Thread.current.thread_variable_set(:redirection_needed, e)
159
+ Thread.current[:redirection_needed] = e
160
160
  rescue StandardError => e
161
- Thread.current.thread_variable_set(:error, e)
161
+ Thread.current[:error] = e
162
162
  end
163
163
  end
164
164
 
165
165
  threads.each(&:join)
166
166
  threads.each do |t|
167
- if t.thread_variable?(:replies)
167
+ if t.key?(:replies)
168
168
  all_replies ||= Array.new(@size)
169
- @pipelines[t.thread_variable_get(:node_key)]
169
+ @pipelines[t[:node_key]]
170
170
  .outer_indices
171
- .each_with_index { |outer, inner| all_replies[outer] = t.thread_variable_get(:replies)[inner] }
172
- elsif t.thread_variable?(:redirection_needed)
171
+ .each_with_index { |outer, inner| all_replies[outer] = t[:replies][inner] }
172
+ elsif t.key?(:redirection_needed)
173
173
  all_replies ||= Array.new(@size)
174
- pipeline = @pipelines[t.thread_variable_get(:node_key)]
175
- err = t.thread_variable_get(:redirection_needed)
174
+ pipeline = @pipelines[t[:node_key]]
175
+ err = t[:redirection_needed]
176
176
  err.indices.each { |i| err.replies[i] = handle_redirection(err.replies[i], pipeline, i) }
177
177
  pipeline.outer_indices.each_with_index { |outer, inner| all_replies[outer] = err.replies[inner] }
178
- elsif t.thread_variable?(:error)
178
+ elsif t.key?(:error)
179
179
  errors ||= {}
180
- errors[t.thread_variable_get(:node_key)] = t.thread_variable_get(:error)
180
+ errors[t[:node_key]] = t[:error]
181
181
  end
182
182
  end
183
183
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'redis_client'
4
+ require 'redis_client/circuit_breaker'
4
5
  require 'redis_client/cluster/command'
5
6
  require 'redis_client/cluster/errors'
6
7
  require 'redis_client/cluster/key_slot_converter'
@@ -54,13 +55,18 @@ class RedisClient
54
55
  node = assign_node(command)
55
56
  try_send(node, method, command, args, &block)
56
57
  end
58
+ rescue ::RedisClient::CircuitBreaker::OpenCircuitError
59
+ raise
57
60
  rescue ::RedisClient::Cluster::Node::ReloadNeeded
58
61
  update_cluster_info!
59
62
  raise ::RedisClient::Cluster::NodeMightBeDown
60
63
  rescue ::RedisClient::Cluster::ErrorCollection => e
64
+ raise if e.errors.any?(::RedisClient::CircuitBreaker::OpenCircuitError)
65
+
61
66
  update_cluster_info! if e.errors.values.any? do |err|
62
67
  err.message.start_with?('CLUSTERDOWN Hash slot not served')
63
68
  end
69
+
64
70
  raise
65
71
  end
66
72
 
@@ -72,6 +78,8 @@ class RedisClient
72
78
  else
73
79
  node.public_send(method, *args, command, &block)
74
80
  end
81
+ rescue ::RedisClient::CircuitBreaker::OpenCircuitError
82
+ raise
75
83
  rescue ::RedisClient::CommandError => e
76
84
  raise if retry_count <= 0
77
85
 
@@ -102,6 +110,8 @@ class RedisClient
102
110
 
103
111
  def try_delegate(node, method, *args, retry_count: 3, **kwargs, &block) # rubocop:disable Metrics/AbcSize
104
112
  node.public_send(method, *args, **kwargs, &block)
113
+ rescue ::RedisClient::CircuitBreaker::OpenCircuitError
114
+ raise
105
115
  rescue ::RedisClient::CommandError => e
106
116
  raise if retry_count <= 0
107
117
 
@@ -197,9 +207,10 @@ class RedisClient
197
207
 
198
208
  private
199
209
 
200
- def send_wait_command(method, command, args, retry_count: 3, &block)
210
+ def send_wait_command(method, command, args, retry_count: 3, &block) # rubocop:disable Metrics/AbcSize
201
211
  @node.call_primaries(method, command, args, &block).select { |r| r.is_a?(Integer) }.sum
202
212
  rescue ::RedisClient::Cluster::ErrorCollection => e
213
+ raise if e.errors.any?(::RedisClient::CircuitBreaker::OpenCircuitError)
203
214
  raise if retry_count <= 0
204
215
  raise if e.errors.values.none? do |err|
205
216
  err.message.include?('WAIT cannot be used with replica instances')
@@ -98,7 +98,7 @@ class RedisClient
98
98
 
99
99
  def build_node_configs(addrs)
100
100
  configs = Array[addrs].flatten.filter_map { |addr| parse_node_addr(addr) }
101
- raise InvalidClientConfigError, '`nodes` option is empty' if configs.size.zero?
101
+ raise InvalidClientConfigError, '`nodes` option is empty' if configs.empty?
102
102
 
103
103
  configs
104
104
  end
@@ -150,7 +150,7 @@ class RedisClient
150
150
  end
151
151
 
152
152
  def merge_generic_config(client_config, node_configs)
153
- return client_config if node_configs.size.zero?
153
+ return client_config if node_configs.empty?
154
154
 
155
155
  cfg = node_configs.first
156
156
  MERGE_CONFIG_KEYS.each { |k| client_config[k] = cfg[k] if cfg.key?(k) }
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.3.15
4
+ version: 0.4.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: 2022-12-21 00:00:00.000000000 Z
11
+ date: 2023-01-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: redis-client
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '0.11'
19
+ version: '0.12'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '0.11'
26
+ version: '0.12'
27
27
  description:
28
28
  email:
29
29
  - proxy0721@gmail.com