redis-cluster-client 0.17.4 → 0.17.6
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 +4 -4
- data/lib/redis_client/cluster/node/base_topology.rb +14 -4
- data/lib/redis_client/cluster/node.rb +19 -1
- data/lib/redis_client/cluster/optimistic_locking.rb +4 -0
- data/lib/redis_client/cluster/pipeline.rb +10 -2
- data/lib/redis_client/cluster/pub_sub.rb +4 -0
- data/lib/redis_client/cluster/router.rb +54 -39
- data/lib/redis_client/cluster/transaction.rb +13 -4
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 675e3b7c851ac7deebc2f261d0aaa7fa6ef4c1865b3382014c41404ca314f5cc
|
|
4
|
+
data.tar.gz: 6a25062f50524661e566a1223752822711cf10246bedec98e52d1bdac3205fcc
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 98cd5427026578e9a175cdcd21def056ca24997213f1c70385c193ce5fa2991a5f60edadb34e557d5c4a75ba4ff985d2c9a8b0c2f7e3e76f22cd9ce529846961
|
|
7
|
+
data.tar.gz: 1f8e2a9cc8576fee87fdfb91485fbb13a5609381ef8f186ae287915f8f51cd4c3611e627e65d82d9c0767d9345a1d62e14a24b0b0a41dd70624558fe3b50e2c5
|
|
@@ -42,6 +42,14 @@ class RedisClient
|
|
|
42
42
|
@replica_clients.freeze
|
|
43
43
|
end
|
|
44
44
|
|
|
45
|
+
def connect_to_node(node_key, option)
|
|
46
|
+
return @clients[node_key] if @clients.key?(node_key)
|
|
47
|
+
|
|
48
|
+
client = build_client(node_key, option)
|
|
49
|
+
@clients = @clients.merge(node_key => client)
|
|
50
|
+
client
|
|
51
|
+
end
|
|
52
|
+
|
|
45
53
|
private
|
|
46
54
|
|
|
47
55
|
def disconnect_from_unwanted_nodes(options)
|
|
@@ -52,13 +60,15 @@ class RedisClient
|
|
|
52
60
|
|
|
53
61
|
def connect_to_new_nodes(options)
|
|
54
62
|
(options.keys - @clients.keys).each do |node_key|
|
|
55
|
-
|
|
56
|
-
config = ::RedisClient::Cluster::Node::Config.new(scale_read: @replica_node_keys.include?(node_key), **option)
|
|
57
|
-
client = @pool.nil? ? config.new_client : config.new_pool(**@pool)
|
|
58
|
-
@clients[node_key] = client
|
|
63
|
+
@clients[node_key] = build_client(node_key, options[node_key])
|
|
59
64
|
end
|
|
60
65
|
end
|
|
61
66
|
|
|
67
|
+
def build_client(node_key, option)
|
|
68
|
+
config = ::RedisClient::Cluster::Node::Config.new(scale_read: @replica_node_keys.include?(node_key), **option.merge(@client_options))
|
|
69
|
+
@pool.nil? ? config.new_client : config.new_pool(**@pool)
|
|
70
|
+
end
|
|
71
|
+
|
|
62
72
|
def make_random(seed)
|
|
63
73
|
# OPTIMIZE: Figure out the most elegant way to pin a node during a pipeline or scan.
|
|
64
74
|
seed.nil? ? Random : Random.new(seed)
|
|
@@ -129,6 +129,22 @@ class RedisClient
|
|
|
129
129
|
@topology.clients.fetch(node_key)
|
|
130
130
|
end
|
|
131
131
|
|
|
132
|
+
def find_or_connect(node_key)
|
|
133
|
+
client = @topology.clients[node_key]
|
|
134
|
+
return client unless client.nil?
|
|
135
|
+
raise ReloadNeeded if node_key.nil? || !@mutex.try_lock
|
|
136
|
+
|
|
137
|
+
begin
|
|
138
|
+
@topology.connect_to_node(node_key, @config.client_config_for_node(node_key))
|
|
139
|
+
ensure
|
|
140
|
+
@mutex.unlock
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def primary_node_key?(node_key)
|
|
145
|
+
@replications.key?(node_key)
|
|
146
|
+
end
|
|
147
|
+
|
|
132
148
|
def call_all(method, command, args, &block)
|
|
133
149
|
call_multiple_nodes!(@topology.clients, method, command, args, &block)
|
|
134
150
|
end
|
|
@@ -155,7 +171,9 @@ class RedisClient
|
|
|
155
171
|
result_values, errors = call_multiple_nodes(@topology.clients, method, command, args, &block)
|
|
156
172
|
return result_values if errors.nil? || errors.empty?
|
|
157
173
|
|
|
158
|
-
raise ReloadNeeded if errors.values.any?
|
|
174
|
+
raise ReloadNeeded if errors.values.any? do |err|
|
|
175
|
+
err.is_a?(::RedisClient::ConnectionError) && !err.is_a?(::RedisClient::CheckoutTimeoutError)
|
|
176
|
+
end
|
|
159
177
|
|
|
160
178
|
raise ::RedisClient::Cluster::ErrorCollection.with_errors(errors)
|
|
161
179
|
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
|
-
|
|
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
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
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
|
-
|
|
109
|
-
|
|
110
|
-
|
|
117
|
+
raise
|
|
118
|
+
rescue ::RedisClient::ConnectionError => e
|
|
119
|
+
raise unless ::RedisClient::Cluster::ErrorIdentification.client_owns_error?(e, node)
|
|
111
120
|
|
|
112
|
-
|
|
113
|
-
|
|
121
|
+
renew_cluster_state
|
|
122
|
+
raise if command.nil? || command.empty?
|
|
114
123
|
|
|
115
|
-
|
|
116
|
-
|
|
124
|
+
retry_count -= 1
|
|
125
|
+
raise if retry_count < 0
|
|
117
126
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
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
|
-
|
|
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
|
|
@@ -225,14 +238,16 @@ class RedisClient
|
|
|
225
238
|
|
|
226
239
|
def assign_redirection_node(err_msg)
|
|
227
240
|
_, slot, node_key = err_msg.split
|
|
228
|
-
|
|
229
|
-
@node.
|
|
230
|
-
handle_node_reload_error { @node.
|
|
241
|
+
# A MOVED to anything but a known primary means the map is behind; the reload is throttled by the jitter.
|
|
242
|
+
renew_cluster_state unless @node.primary_node_key?(node_key)
|
|
243
|
+
node = handle_node_reload_error { @node.find_or_connect(node_key) }
|
|
244
|
+
@node.update_slot(slot.to_i, node_key)
|
|
245
|
+
node
|
|
231
246
|
end
|
|
232
247
|
|
|
233
248
|
def assign_asking_node(err_msg)
|
|
234
249
|
_, _, node_key = err_msg.split
|
|
235
|
-
handle_node_reload_error { @node.
|
|
250
|
+
handle_node_reload_error { @node.find_or_connect(node_key) }
|
|
236
251
|
end
|
|
237
252
|
|
|
238
253
|
def node_keys
|
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|