Skip to content

Commit a943693

Browse files
committed
Hiredis+sentinel don't re-use connections after a failover
Fix: #257 The `hiredis` C library isn't capable of reconnecting to a different server, only to the same server. For classic client it's not a big deal, but with sentinel, a failover may have happened, hence the host or port may have changed.
1 parent 6807a3a commit a943693

File tree

8 files changed

+24
-11
lines changed

8 files changed

+24
-11
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: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ def write_multi(commands)
131131
private
132132

133133
def connect
134+
@server_key = @config.server_key
134135
_connect(@config.path, @config.host, @config.port, @config.ssl_context)
135136
rescue SystemCallError => error
136137
host = @path || "#{@host}:#{@port}"

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: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,7 @@ def initialize(config)
99
@pending_reads = 0
1010
@retry_attempt = nil
1111
@config = config
12-
@path = @config.path
13-
@host = @config.host
14-
@port = @config.port
12+
@server_key = nil
1513
end
1614

1715
def reconnect
@@ -25,7 +23,7 @@ def close
2523
end
2624

2725
def revalidate
28-
if @pending_reads > 0 && !config_changed?
26+
if @pending_reads > 0 || @server_key != @config.server_key
2927
close
3028
false
3129
else
@@ -102,11 +100,5 @@ def connection_error(message)
102100
error._set_retry_attempt(@retry_attempt)
103101
error
104102
end
105-
106-
private
107-
108-
def config_changed?
109-
!(@path == @config.path && @host == @config.host && @port == @config.port)
110-
end
111103
end
112104
end

lib/redis_client/ruby_connection.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ def measure_round_trip_delay
109109
private
110110

111111
def connect
112+
@server_key = @config.server_key
112113
socket = if @config.path
113114
UNIXSocket.new(@config.path)
114115
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)