Skip to content

Commit 72c72d4

Browse files
authored
Merge pull request #260 from redis-rb/hiredis-reconnect
Hiredis+sentinel don't re-use connections after a failover
2 parents 954eab2 + 2e13847 commit 72c72d4

File tree

9 files changed

+30
-12
lines changed

9 files changed

+30
-12
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# Unreleased
22

3+
- hiredis-client: Properly reconnect to the new leader after a sentinel failover.
4+
35
# 0.26.0
46

57
- Add `RedisClient::Error#final?` and `#retriable?` to allow middleware to filter out non-final errors.

hiredis-client/lib/redis_client/hiredis_connection.rb

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,8 @@ def initialize(ca_file: nil, ca_path: nil, cert: nil, key: nil, hostname: nil)
3939
end
4040
end
4141

42-
attr_reader :config
43-
4442
def initialize(config, connect_timeout:, read_timeout:, write_timeout:)
45-
super()
46-
@config = config
43+
super(config)
4744
self.connect_timeout = connect_timeout
4845
self.read_timeout = read_timeout
4946
self.write_timeout = write_timeout
@@ -134,6 +131,7 @@ def write_multi(commands)
134131
private
135132

136133
def connect
134+
@server_key = @config.server_key
137135
_connect(@config.path, @config.host, @config.port, @config.ssl_context)
138136
rescue SystemCallError => error
139137
host = @config.path || "#{@config.host}:#{@config.port}"

lib/redis_client.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -786,7 +786,7 @@ def raw_connection
786786
def connect
787787
@pid = PIDCache.pid
788788

789-
if @raw_connection
789+
if @raw_connection&.revalidate
790790
@middlewares.connect(config) do
791791
@raw_connection.reconnect
792792
end

lib/redis_client/config.rb

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ def server_url
186186

187187
include Common
188188

189-
attr_reader :host, :port, :path
189+
attr_reader :host, :port, :path, :server_key
190190

191191
def initialize(
192192
url: nil,
@@ -220,6 +220,8 @@ def initialize(
220220
@host = host || DEFAULT_HOST
221221
@port = Integer(port || DEFAULT_PORT)
222222
end
223+
224+
@server_key = [@path, @host, @port].freeze
223225
end
224226
end
225227
end

lib/redis_client/connection_mixin.rb

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,13 @@
33
class RedisClient
44
module ConnectionMixin
55
attr_accessor :retry_attempt
6+
attr_reader :config
67

7-
def initialize
8+
def initialize(config)
89
@pending_reads = 0
910
@retry_attempt = nil
11+
@config = config
12+
@server_key = nil
1013
end
1114

1215
def reconnect
@@ -20,7 +23,7 @@ def close
2023
end
2124

2225
def revalidate
23-
if @pending_reads > 0
26+
if @pending_reads > 0 || @server_key != @config.server_key
2427
close
2528
false
2629
else

lib/redis_client/ruby_connection.rb

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,8 @@ def ssl_context(ssl_params)
4040

4141
SUPPORTS_RESOLV_TIMEOUT = Socket.method(:tcp).parameters.any? { |p| p.last == :resolv_timeout }
4242

43-
attr_reader :config
44-
4543
def initialize(config, connect_timeout:, read_timeout:, write_timeout:)
46-
super()
47-
@config = config
44+
super(config)
4845
@connect_timeout = connect_timeout
4946
@read_timeout = read_timeout
5047
@write_timeout = write_timeout
@@ -112,6 +109,7 @@ def measure_round_trip_delay
112109
private
113110

114111
def connect
112+
@server_key = @config.server_key
115113
socket = if @config.path
116114
UNIXSocket.new(@config.path)
117115
else

lib/redis_client/sentinel_config.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,10 @@ def reset
8282
end
8383
end
8484

85+
def server_key
86+
config.server_key
87+
end
88+
8589
def host
8690
config.host
8791
end

test/redis_client/connection_test.rb

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,15 @@ def test_reconnect_reuse
205205
end
206206
end
207207

208+
def test_reconnect_config_change
209+
assert_equal "PONG", @redis.call("PING")
210+
@redis.close
211+
@redis.instance_variable_set(:@config, RedisClient.config(**tcp_config, port: 1))
212+
assert_raises RedisClient::CannotConnectError do
213+
@redis.call("PING")
214+
end
215+
end
216+
208217
def test_circuit_breaker
209218
circuit_breaker = CircuitBreaker.new(error_threshold: 3, success_threshold: 2, error_timeout: 1)
210219
@redis = new_client(circuit_breaker: circuit_breaker)

test/sentinel/sentinel_test.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,9 +147,11 @@ def test_master_failover_ready
147147
stub(@config, :sentinel_client, ->(_config) { sentinel_client_mock }) do
148148
client = @config.new_client
149149
assert_equal "PONG", client.call("PING")
150+
initial_server_key = @config.server_key
150151

151152
Toxiproxy[Servers::REDIS.name].down do
152153
assert_equal "PONG", client.call("PING")
154+
refute_equal initial_server_key, @config.server_key
153155
end
154156
end
155157
ensure

0 commit comments

Comments
 (0)