redis-cluster-client 0.16.0 → 0.16.2
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/command.rb +2 -1
- data/lib/redis_client/cluster/node.rb +8 -3
- data/lib/redis_client/cluster/node_key.rb +19 -1
- data/lib/redis_client/cluster/optimistic_locking.rb +1 -1
- data/lib/redis_client/cluster/pub_sub.rb +51 -4
- data/lib/redis_client/cluster_config.rb +15 -1
- 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: f8d639d8939c9fdf304fa0f61886c1274f6e1c08bcd9e213dae3c7e0d844be02
|
|
4
|
+
data.tar.gz: 6f540ce88572df689c8a3e3971e3e96054b09b9fee7ef53c9aa518566ec3dd75
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7d9c164a6590fb0ab3fa43328b484f8919895f17d3a6d5a9114c0c7b1a7867864be6d8e12ae1af45cdc81d39b9aa590e8c088de878ab964c18d9ae6f3c58682d
|
|
7
|
+
data.tar.gz: 01c0503f5f75d2441bdd2ea71537114c380db552e00ed8b130b0330626440f58a0d94bf94974dc044f4aebca9e024c931635dd8cbe5c96e225c80a7e10120840
|
|
@@ -30,13 +30,14 @@ class RedisClient
|
|
|
30
30
|
regular_timeout = node.read_timeout
|
|
31
31
|
node.read_timeout = slow_command_timeout > 0.0 ? slow_command_timeout : regular_timeout
|
|
32
32
|
reply = node.call('command')
|
|
33
|
-
node.read_timeout = regular_timeout
|
|
34
33
|
commands = parse_command_reply(reply)
|
|
35
34
|
cmd = ::RedisClient::Cluster::Command.new(commands)
|
|
36
35
|
break
|
|
37
36
|
rescue ::RedisClient::Error => e
|
|
38
37
|
errors ||= []
|
|
39
38
|
errors << e
|
|
39
|
+
ensure
|
|
40
|
+
node.read_timeout = regular_timeout
|
|
40
41
|
end
|
|
41
42
|
|
|
42
43
|
return cmd unless cmd.nil?
|
|
@@ -405,7 +405,10 @@ class RedisClient
|
|
|
405
405
|
|
|
406
406
|
def parse_cluster_shards_reply(reply) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
407
407
|
reply.each_with_object([]) do |shard, acc|
|
|
408
|
+
resp2 = shard.is_a?(Array)
|
|
409
|
+
shard = shard.each_slice(2).to_h if resp2
|
|
408
410
|
nodes = shard.fetch('nodes')
|
|
411
|
+
nodes = nodes.map { |n| n.each_slice(2).to_h } if resp2
|
|
409
412
|
primary_id = nodes.find { |n| n.fetch('role') == 'master' }.fetch('id')
|
|
410
413
|
|
|
411
414
|
nodes.each do |node|
|
|
@@ -476,9 +479,11 @@ class RedisClient
|
|
|
476
479
|
def with_reload_jitter
|
|
477
480
|
return unless @next_reload_time.nil? || obtain_current_time >= @next_reload_time
|
|
478
481
|
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
+
begin
|
|
483
|
+
yield
|
|
484
|
+
ensure
|
|
485
|
+
@next_reload_time = obtain_current_time + @random.rand(JITTER_WINDOW)
|
|
486
|
+
end
|
|
482
487
|
end
|
|
483
488
|
|
|
484
489
|
def with_reload_lock
|
|
@@ -18,12 +18,30 @@ class RedisClient
|
|
|
18
18
|
end
|
|
19
19
|
|
|
20
20
|
def split(node_key)
|
|
21
|
-
|
|
21
|
+
return [node_key, nil] if node_key.nil? || node_key.empty?
|
|
22
|
+
|
|
23
|
+
bracketed = split_bracketed(node_key)
|
|
24
|
+
return bracketed unless bracketed.nil?
|
|
25
|
+
|
|
26
|
+
pos = node_key.rindex(DELIMITER, -1)
|
|
22
27
|
return [node_key, nil] if pos.nil?
|
|
23
28
|
|
|
24
29
|
[node_key[0, pos], node_key[(pos + 1)..]]
|
|
25
30
|
end
|
|
26
31
|
|
|
32
|
+
def split_bracketed(node_key)
|
|
33
|
+
return nil unless node_key.start_with?('[')
|
|
34
|
+
|
|
35
|
+
end_bracket = node_key.index(']')
|
|
36
|
+
return nil if end_bracket.nil?
|
|
37
|
+
|
|
38
|
+
host = node_key[1, end_bracket - 1]
|
|
39
|
+
remainder = node_key[(end_bracket + 1)..]
|
|
40
|
+
port = remainder.start_with?(DELIMITER) ? remainder[1..] : nil
|
|
41
|
+
[host, port]
|
|
42
|
+
end
|
|
43
|
+
private_class_method :split_bracketed
|
|
44
|
+
|
|
27
45
|
def build_from_uri(uri)
|
|
28
46
|
return '' if uri.nil?
|
|
29
47
|
|
|
@@ -54,7 +54,7 @@ class RedisClient
|
|
|
54
54
|
rescue ::RedisClient::ConnectionError
|
|
55
55
|
# Deduct the number of retries that happened _inside_ router#handle_redirection from our remaining
|
|
56
56
|
# _external_ retries. Always deduct at least one in case handle_redirection raises without trying the block.
|
|
57
|
-
retry_count -= [times_block_executed, 1].min
|
|
57
|
+
retry_count -= times_block_executed == 0 ? 1 : [times_block_executed, 1].min
|
|
58
58
|
raise if retry_count < 0
|
|
59
59
|
|
|
60
60
|
retry
|
|
@@ -61,7 +61,17 @@ class RedisClient
|
|
|
61
61
|
end
|
|
62
62
|
|
|
63
63
|
BUF_SIZE = Integer(ENV.fetch('REDIS_CLIENT_PUBSUB_BUF_SIZE', 1024))
|
|
64
|
-
|
|
64
|
+
RECOVERY_BASE_INTERVAL = Float(ENV.fetch('REDIS_CLIENT_PUBSUB_RECOVERY_INTERVAL_SEC', 1.0))
|
|
65
|
+
RECOVERY_MAX_INTERVAL = Float(ENV.fetch('REDIS_CLIENT_PUBSUB_RECOVERY_MAX_INTERVAL_SEC', 30.0))
|
|
66
|
+
RECOVERY_MAX_ATTEMPTS = Integer(ENV.fetch('REDIS_CLIENT_PUBSUB_RECOVERY_MAX_ATTEMPTS', 10))
|
|
67
|
+
SUBSCRIBE_COMMANDS = %w[subscribe psubscribe ssubscribe].freeze
|
|
68
|
+
UNSUBSCRIBE_TO_SUBSCRIBE = {
|
|
69
|
+
'unsubscribe' => 'subscribe',
|
|
70
|
+
'punsubscribe' => 'psubscribe',
|
|
71
|
+
'sunsubscribe' => 'ssubscribe'
|
|
72
|
+
}.freeze
|
|
73
|
+
private_constant :BUF_SIZE, :RECOVERY_BASE_INTERVAL, :RECOVERY_MAX_INTERVAL, :RECOVERY_MAX_ATTEMPTS,
|
|
74
|
+
:SUBSCRIBE_COMMANDS, :UNSUBSCRIBE_TO_SUBSCRIBE
|
|
65
75
|
|
|
66
76
|
def initialize(router, command_builder)
|
|
67
77
|
@router = router
|
|
@@ -74,14 +84,14 @@ class RedisClient
|
|
|
74
84
|
def call(*args, **kwargs)
|
|
75
85
|
command = @command_builder.generate(args, kwargs)
|
|
76
86
|
_call(command)
|
|
77
|
-
|
|
87
|
+
remember(command)
|
|
78
88
|
nil
|
|
79
89
|
end
|
|
80
90
|
|
|
81
91
|
def call_v(command)
|
|
82
92
|
command = @command_builder.generate(command)
|
|
83
93
|
_call(command)
|
|
84
|
-
|
|
94
|
+
remember(command)
|
|
85
95
|
nil
|
|
86
96
|
end
|
|
87
97
|
|
|
@@ -118,6 +128,35 @@ class RedisClient
|
|
|
118
128
|
|
|
119
129
|
private
|
|
120
130
|
|
|
131
|
+
def remember(command)
|
|
132
|
+
cmd = command.first.to_s.downcase
|
|
133
|
+
if SUBSCRIBE_COMMANDS.include?(cmd)
|
|
134
|
+
@commands << command
|
|
135
|
+
elsif (subscribe_cmd = UNSUBSCRIBE_TO_SUBSCRIBE[cmd])
|
|
136
|
+
forget_subscriptions(subscribe_cmd, command[1..])
|
|
137
|
+
else
|
|
138
|
+
@commands << command
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def forget_subscriptions(subscribe_cmd, channels)
|
|
143
|
+
if channels.nil? || channels.empty?
|
|
144
|
+
@commands.reject! { |c| c.first.to_s.casecmp(subscribe_cmd).zero? }
|
|
145
|
+
else
|
|
146
|
+
@commands.reject! { |c| prune_entry(c, subscribe_cmd, channels) }
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def prune_entry(entry, subscribe_cmd, channels)
|
|
151
|
+
return false unless entry.first.to_s.casecmp(subscribe_cmd).zero?
|
|
152
|
+
|
|
153
|
+
remaining = entry[1..] - channels
|
|
154
|
+
return true if remaining.empty?
|
|
155
|
+
|
|
156
|
+
entry.replace([entry.first, *remaining])
|
|
157
|
+
false
|
|
158
|
+
end
|
|
159
|
+
|
|
121
160
|
def _call(command) # rubocop:disable Metrics/AbcSize
|
|
122
161
|
if command.first.casecmp('subscribe').zero?
|
|
123
162
|
call_to_single_state(command)
|
|
@@ -179,6 +218,7 @@ class RedisClient
|
|
|
179
218
|
end
|
|
180
219
|
|
|
181
220
|
def start_over
|
|
221
|
+
attempt = 0
|
|
182
222
|
loop do
|
|
183
223
|
@router.renew_cluster_state
|
|
184
224
|
@state_dict.each_value(&:close)
|
|
@@ -186,9 +226,16 @@ class RedisClient
|
|
|
186
226
|
@commands.each { |command| _call(command) }
|
|
187
227
|
break
|
|
188
228
|
rescue ::RedisClient::ConnectionError, ::RedisClient::Cluster::NodeMightBeDown
|
|
189
|
-
|
|
229
|
+
attempt += 1
|
|
230
|
+
raise if attempt >= RECOVERY_MAX_ATTEMPTS
|
|
231
|
+
|
|
232
|
+
sleep recovery_interval(attempt)
|
|
190
233
|
end
|
|
191
234
|
end
|
|
235
|
+
|
|
236
|
+
def recovery_interval(attempt)
|
|
237
|
+
[RECOVERY_BASE_INTERVAL * (2**(attempt - 1)), RECOVERY_MAX_INTERVAL].min
|
|
238
|
+
end
|
|
192
239
|
end
|
|
193
240
|
end
|
|
194
241
|
end
|
|
@@ -32,6 +32,12 @@ class RedisClient
|
|
|
32
32
|
|
|
33
33
|
InvalidClientConfigError = Class.new(::RedisClient::Cluster::Error)
|
|
34
34
|
|
|
35
|
+
SENSITIVE_INSPECT_KEYS = %i[username password].freeze
|
|
36
|
+
INSPECT_REDACTED_KEYS = %i[command_builder].freeze
|
|
37
|
+
INSPECT_PLACEHOLDER = '[FILTERED]'
|
|
38
|
+
|
|
39
|
+
private_constant :SENSITIVE_INSPECT_KEYS, :INSPECT_REDACTED_KEYS, :INSPECT_PLACEHOLDER
|
|
40
|
+
|
|
35
41
|
attr_reader :command_builder, :client_config, :replica_affinity, :slow_command_timeout,
|
|
36
42
|
:connect_with_original_config, :startup_nodes, :max_startup_sample, :id
|
|
37
43
|
|
|
@@ -65,7 +71,7 @@ class RedisClient
|
|
|
65
71
|
end
|
|
66
72
|
|
|
67
73
|
def inspect
|
|
68
|
-
"#<#{self.class.name} #{startup_nodes.values.map { |v| v
|
|
74
|
+
"#<#{self.class.name} #{startup_nodes.values.map { |v| redact_for_inspect(v) }}>"
|
|
69
75
|
end
|
|
70
76
|
|
|
71
77
|
def connect_timeout
|
|
@@ -117,6 +123,14 @@ class RedisClient
|
|
|
117
123
|
|
|
118
124
|
private
|
|
119
125
|
|
|
126
|
+
def redact_for_inspect(node_config)
|
|
127
|
+
node_config.each_with_object({}) do |(key, value), redacted|
|
|
128
|
+
next if INSPECT_REDACTED_KEYS.include?(key)
|
|
129
|
+
|
|
130
|
+
redacted[key] = SENSITIVE_INSPECT_KEYS.include?(key) ? INSPECT_PLACEHOLDER : value
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
120
134
|
def merge_concurrency_option(option)
|
|
121
135
|
opts = {}
|
|
122
136
|
|