redis-cluster-client 0.17.3 → 0.17.5

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: ee9d2adeb89553bf3e1bfdb3d98f1dabed5a9e4347bee756776081036a1d4341
4
- data.tar.gz: 9576cdbb916ee5b1ea82b722bb41a8144f46bbd4641620c41822c89be85bcf34
3
+ metadata.gz: fb60940c0dbbb1bbf6497debd026596a02223668a8c7fa39b48055f5461db379
4
+ data.tar.gz: ffbd5eaec4b8d29637d0a4d65053039e81a08477a28d1b9e201ddba355030eae
5
5
  SHA512:
6
- metadata.gz: 99ba847297a711cb7f1af1c05da9cf4222d00c3c89a91e4ba92b8e7eb5078275db448b46ce573b97e9fa03e40fd7cb69deac0987a43fc6be3dc3b53ea9779024
7
- data.tar.gz: 8755ce77dfad1fc86fdb17ce63a31aa5eb71fa8aba44a84eea3194c29d2df2045dfeab48d60137d9e1032ec1cfc42a66aef732c48ba005f38da4a687c084ec8a
6
+ metadata.gz: 4d107b60e99745a2303b0c2f7fc702baa955f4f52a7873bd6bf11ef885dbbebdb461dd8c85f6ff4387e4460a511daed69f33a813d1dac851981dd34bb25a6128
7
+ data.tar.gz: bd9515f510ffe274f2e30c0fefb47729425a7a1d7a11c52f3b1ba1c331b5fd747c6a5398e553952bcf4a935f05919811e02e838ba76462b65a259e35d580ea94
@@ -155,7 +155,9 @@ class RedisClient
155
155
  result_values, errors = call_multiple_nodes(@topology.clients, method, command, args, &block)
156
156
  return result_values if errors.nil? || errors.empty?
157
157
 
158
- raise ReloadNeeded if errors.values.any?(::RedisClient::ConnectionError)
158
+ raise ReloadNeeded if errors.values.any? do |err|
159
+ err.is_a?(::RedisClient::ConnectionError) && !err.is_a?(::RedisClient::CheckoutTimeoutError)
160
+ end
159
161
 
160
162
  raise ::RedisClient::Cluster::ErrorCollection.with_errors(errors)
161
163
  end
@@ -34,6 +34,8 @@ class RedisClient
34
34
  @router.renew_cluster_state if e.message.start_with?('CLUSTERDOWN')
35
35
  raise
36
36
  end
37
+ rescue ::RedisClient::CheckoutTimeoutError
38
+ raise
37
39
  rescue ::RedisClient::ConnectionError
38
40
  @router.renew_cluster_state
39
41
  raise
@@ -52,6 +54,8 @@ class RedisClient
52
54
  times_block_executed += 1
53
55
  handle_asking_once(nd, &blk)
54
56
  end
57
+ rescue ::RedisClient::CheckoutTimeoutError
58
+ raise
55
59
  rescue ::RedisClient::ConnectionError
56
60
  # Deduct the number of retries that happened _inside_ router#handle_redirection from our remaining
57
61
  # _external_ retries. Always deduct at least one in case handle_redirection raises without trying the block.
@@ -205,7 +205,7 @@ class RedisClient
205
205
  cluster_state_errors ||= {}
206
206
  cluster_state_errors[node_key] = v
207
207
  when StandardError
208
- cluster_state_errors ||= {} if v.is_a?(::RedisClient::ConnectionError)
208
+ cluster_state_errors ||= {} if v.is_a?(::RedisClient::ConnectionError) && !v.is_a?(::RedisClient::CheckoutTimeoutError)
209
209
  errors ||= {}
210
210
  errors[node_key] = v
211
211
  else
@@ -277,10 +277,16 @@ class RedisClient
277
277
  try_redirection(node, pipeline, inner_index)
278
278
  elsif err.message.start_with?('ASK')
279
279
  node = @router.assign_asking_node(err.message)
280
- try_asking(node) ? try_redirection(node, pipeline, inner_index) : err
280
+ node.with do |client|
281
+ try_asking(client) ? try_redirection(client, pipeline, inner_index) : err
282
+ end
281
283
  else
282
284
  err
283
285
  end
286
+ rescue ::RedisClient::CheckoutTimeoutError
287
+ raise if @exception
288
+
289
+ err
284
290
  end
285
291
 
286
292
  def try_redirection(node, pipeline, inner_index)
@@ -305,6 +311,8 @@ class RedisClient
305
311
 
306
312
  def try_asking(node)
307
313
  node.call('asking') == 'OK'
314
+ rescue ::RedisClient::CheckoutTimeoutError
315
+ raise
308
316
  rescue StandardError
309
317
  false
310
318
  end
@@ -171,6 +171,8 @@ class RedisClient
171
171
 
172
172
  def handle_connection_error(node_key, ignore: false)
173
173
  yield
174
+ rescue ::RedisClient::CheckoutTimeoutError
175
+ raise
174
176
  rescue ::RedisClient::ConnectionError
175
177
  @state_dict[node_key]&.close
176
178
  @state_dict.delete(node_key)
@@ -186,6 +188,8 @@ class RedisClient
186
188
  @state_dict.clear
187
189
  @commands.each { |command| _call(command) }
188
190
  break
191
+ rescue ::RedisClient::CheckoutTimeoutError
192
+ raise
189
193
  rescue ::RedisClient::ConnectionError, ::RedisClient::Cluster::NodeMightBeDown
190
194
  attempt += 1
191
195
  raise if attempt >= 10
@@ -42,7 +42,7 @@ class RedisClient
42
42
  return send_command_by_spec(method, command, args, &block) if action.nil?
43
43
 
44
44
  send_command_by_action(action, method, command, args, &block)
45
- rescue ::RedisClient::CircuitBreaker::OpenCircuitError
45
+ rescue ::RedisClient::CircuitBreaker::OpenCircuitError, ::RedisClient::CheckoutTimeoutError
46
46
  raise
47
47
  rescue ::RedisClient::Cluster::Node::ReloadNeeded
48
48
  renew_cluster_state
@@ -58,6 +58,7 @@ class RedisClient
58
58
  raise if e.errors.any?(::RedisClient::CircuitBreaker::OpenCircuitError)
59
59
 
60
60
  renew_cluster_state if e.errors.values.any? do |err|
61
+ next false if err.is_a?(::RedisClient::CheckoutTimeoutError)
61
62
  next false if ::RedisClient::Cluster::ErrorIdentification.identifiable?(err) && @node.none? { |c| ::RedisClient::Cluster::ErrorIdentification.client_owns_error?(err, c) }
62
63
 
63
64
  err.message.start_with?('CLUSTERDOWN') || err.is_a?(::RedisClient::ConnectionError)
@@ -84,46 +85,56 @@ class RedisClient
84
85
  end
85
86
 
86
87
  def handle_redirection(node, command, retry_count:) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
87
- yield node
88
- rescue ::RedisClient::CircuitBreaker::OpenCircuitError
89
- raise
90
- rescue ::RedisClient::CommandError => e
91
- raise unless ::RedisClient::Cluster::ErrorIdentification.client_owns_error?(e, node)
92
-
93
- retry_count -= 1
94
- if e.message.start_with?('MOVED')
95
- node = assign_redirection_node(e.message)
96
- retry if retry_count >= 0
97
- elsif e.message.start_with?('ASK')
98
- node = assign_asking_node(e.message)
99
- if retry_count >= 0
100
- node.call('asking')
101
- retry
88
+ asking = false
89
+ begin
90
+ if asking
91
+ node.with do |client|
92
+ client.call('asking')
93
+ yield client
94
+ end
95
+ else
96
+ yield node
97
+ end
98
+ rescue ::RedisClient::CircuitBreaker::OpenCircuitError, ::RedisClient::CheckoutTimeoutError
99
+ raise
100
+ rescue ::RedisClient::CommandError => e
101
+ raise unless ::RedisClient::Cluster::ErrorIdentification.client_owns_error?(e, node)
102
+
103
+ retry_count -= 1
104
+ asking = false
105
+ if e.message.start_with?('MOVED')
106
+ node = assign_redirection_node(e.message)
107
+ retry if retry_count >= 0
108
+ elsif e.message.start_with?('ASK')
109
+ node = assign_asking_node(e.message)
110
+ asking = true
111
+ retry if retry_count >= 0
112
+ elsif e.message.start_with?('CLUSTERDOWN')
113
+ renew_cluster_state
114
+ retry if retry_count >= 0
102
115
  end
103
- elsif e.message.start_with?('CLUSTERDOWN')
104
- renew_cluster_state
105
- retry if retry_count >= 0
106
- end
107
116
 
108
- raise
109
- rescue ::RedisClient::ConnectionError => e
110
- raise unless ::RedisClient::Cluster::ErrorIdentification.client_owns_error?(e, node)
117
+ raise
118
+ rescue ::RedisClient::ConnectionError => e
119
+ raise unless ::RedisClient::Cluster::ErrorIdentification.client_owns_error?(e, node)
111
120
 
112
- renew_cluster_state
113
- raise if command.nil? || command.empty?
121
+ renew_cluster_state
122
+ raise if command.nil? || command.empty?
114
123
 
115
- retry_count -= 1
116
- raise if retry_count < 0
124
+ retry_count -= 1
125
+ raise if retry_count < 0
117
126
 
118
- # Find the node to use for this command - if this fails for some reason, though, re-use
119
- # the old node.
120
- begin
121
- node = find_node(find_node_key(command))
122
- rescue StandardError
123
- raise e
124
- end
127
+ # Find the node to use for this command - if this fails for some reason, though, re-use
128
+ # the old node.
129
+ begin
130
+ node = find_node(find_node_key(command))
131
+ rescue StandardError
132
+ raise e
133
+ end
125
134
 
126
- retry
135
+ asking = false
136
+ retry
137
+ end
127
138
  end
128
139
 
129
140
  def scan(command, seed: nil, cursor_index: 1) # rubocop:disable Metrics/AbcSize
@@ -146,6 +157,8 @@ class RedisClient
146
157
  client_index += 1 if result_cursor == 0
147
158
 
148
159
  [((result_cursor << 8) + client_index).to_s, result_keys]
160
+ rescue ::RedisClient::CheckoutTimeoutError
161
+ raise
149
162
  rescue ::RedisClient::ConnectionError
150
163
  renew_cluster_state
151
164
  raise
@@ -351,7 +364,8 @@ class RedisClient
351
364
  elsif command[1].casecmp('purge').zero?
352
365
  @node.call_all(method, command, args).first.then(&TSF.call(block))
353
366
  else
354
- assign_node(command).public_send(method, *args, command, &block)
367
+ # The other subcommands keep the default routing with the redirection handling.
368
+ send_command_by_spec(method, command, args, &block)
355
369
  end
356
370
  end
357
371
 
@@ -360,12 +374,15 @@ class RedisClient
360
374
  @node.call_all(method, command, args, &block).flatten
361
375
  elsif command[1].casecmp('pause').zero?
362
376
  @node.call_all(method, command, args).first.then(&TSF.call(block))
377
+ elsif command[1].casecmp('unpause').zero?
378
+ @node.call_all(method, command, args).first.then(&TSF.call(block))
363
379
  elsif command[1].casecmp('reply').zero?
364
380
  @node.call_all(method, command, args).first.then(&TSF.call(block))
365
381
  elsif command[1].casecmp('setname').zero?
366
382
  @node.call_all(method, command, args).first.then(&TSF.call(block))
367
383
  else
368
- assign_node(command).public_send(method, *args, command, &block)
384
+ # The other subcommands keep the default routing with the command tips.
385
+ send_command_by_spec(method, command, args, &block)
369
386
  end
370
387
  end
371
388
 
@@ -376,8 +393,15 @@ class RedisClient
376
393
  command[1].casecmp('slots').zero? ||
377
394
  command[1].casecmp('shards').zero? ||
378
395
  command[1].casecmp('count-failure-reports').zero? ||
379
- command[1].casecmp('slaves').zero?
396
+ command[1].casecmp('slaves').zero? ||
397
+ command[1].casecmp('replicas').zero? ||
398
+ command[1].casecmp('myid').zero? ||
399
+ command[1].casecmp('myshardid').zero? ||
400
+ command[1].casecmp('links').zero?
380
401
  assign_node(command).public_send(method, *args, command, &block)
402
+ elsif command[1].casecmp('slot-stats').zero?
403
+ # Each primary replies with the statistics of its own slots only.
404
+ @node.call_primaries(method, command, args).flatten(1).then(&TSF.call(block))
381
405
  elsif command[1].casecmp('saveconfig').zero?
382
406
  @node.call_all(method, command, args).first.then(&TSF.call(block))
383
407
  elsif command[1].casecmp('countkeysinslot').zero? || command[1].casecmp('getkeysinslot').zero?
@@ -394,15 +418,18 @@ class RedisClient
394
418
  if command[1].casecmp('debug').zero?
395
419
  @node.call_all(method, command, args).first.then(&TSF.call(block))
396
420
  elsif command[1].casecmp('kill').zero?
397
- @node.call_all(method, command, args).first.then(&TSF.call(block))
421
+ # The primaries without any running script reply with the NOTBUSY error.
422
+ @node.call_primaries_leniently(method, command, args).first.then(&TSF.call(block))
398
423
  elsif command[1].casecmp('flush').zero?
399
- @node.call_primaries(method, command, args).first.then(&TSF.call(block))
424
+ # The redis 7.0 or later doesn't propagate the scripts to the replicas.
425
+ @node.call_all(method, command, args).first.then(&TSF.call(block))
400
426
  elsif command[1].casecmp('load').zero?
401
- @node.call_primaries(method, command, args).first.then(&TSF.call(block))
427
+ @node.call_all(method, command, args).first.then(&TSF.call(block))
402
428
  elsif command[1].casecmp('exists').zero?
403
429
  @node.call_all(method, command, args).transpose.map { |arr| arr.any?(&:zero?) ? 0 : 1 }.then(&TSF.call(block))
404
430
  else
405
- assign_node(command).public_send(method, *args, command, &block)
431
+ # The other subcommands keep the default routing with the redirection handling.
432
+ send_command_by_spec(method, command, args, &block)
406
433
  end
407
434
  end
408
435
 
@@ -124,22 +124,27 @@ class RedisClient
124
124
  end
125
125
  end
126
126
 
127
- def send_pipeline(client, redirect:) # rubocop:disable Metrics/AbcSize
127
+ def send_pipeline(client, redirect:) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity
128
128
  replies = client.ensure_connected_cluster_scoped(retryable: @retryable) do |connection|
129
129
  commands = @pipeline._commands
130
130
  client.middlewares.call_pipelined(commands, client.config) do
131
131
  connection.call_pipelined(commands, nil)
132
132
  rescue ::RedisClient::CommandError => e
133
133
  ensure_the_same_slot!(commands)
134
- return handle_command_error!(e, redirect: redirect) unless redirect.zero?
134
+ raise if redirect.zero?
135
+ # Follow the redirection after leaving this block, so that errors from the other node don't close or retry this connection.
136
+ break e if e.message.start_with?('MOVED', 'ASK')
135
137
 
136
- raise
138
+ handle_command_error!(e, redirect: redirect)
137
139
  end
138
140
  end
139
141
 
142
+ return handle_command_error!(replies, redirect: redirect) if replies.is_a?(::RedisClient::CommandError)
140
143
  return if replies.last.nil?
141
144
 
142
145
  coerce_results!(replies.last)
146
+ rescue ::RedisClient::CheckoutTimeoutError
147
+ raise
143
148
  rescue ::RedisClient::ConnectionError
144
149
  @router.renew_cluster_state if @watching_slot.nil?
145
150
  raise
@@ -171,7 +176,9 @@ class RedisClient
171
176
  send_transaction(node, redirect: redirect - 1)
172
177
  elsif err.message.start_with?('ASK')
173
178
  node = @router.assign_asking_node(err.message)
174
- try_asking(node) ? send_transaction(node, redirect: redirect - 1) : raise(err)
179
+ node.with do |client|
180
+ try_asking(client) ? send_transaction(client, redirect: redirect - 1) : raise(err)
181
+ end
175
182
  elsif err.message.start_with?('CLUSTERDOWN')
176
183
  @router.renew_cluster_state if @watching_slot.nil?
177
184
  raise err
@@ -190,6 +197,8 @@ class RedisClient
190
197
 
191
198
  def try_asking(node)
192
199
  node.call('asking') == 'OK'
200
+ rescue ::RedisClient::CheckoutTimeoutError
201
+ raise
193
202
  rescue StandardError
194
203
  false
195
204
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: redis-cluster-client
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.17.3
4
+ version: 0.17.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Taishi Kasuga