Skip to content

Commit c5c91fc

Browse files
supercaracalbyroot
authored andcommitted
Ignore Sentinel slaves that are flaged as down (#829)
1 parent feae68c commit c5c91fc

File tree

2 files changed

+47
-4
lines changed

2 files changed

+47
-4
lines changed

lib/redis/client.rb

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
# frozen_string_literal: true
2+
13
require_relative "errors"
24
require "socket"
35
require "cgi"
@@ -349,7 +351,8 @@ def establish_connection
349351
Errno::EHOSTUNREACH,
350352
Errno::ENETUNREACH,
351353
Errno::ENOENT,
352-
Errno::ETIMEDOUT
354+
Errno::ETIMEDOUT,
355+
Errno::EINVAL
353356

354357
raise CannotConnectError, "Error connecting to Redis on #{location} (#{$!.class})"
355358
end
@@ -604,9 +607,19 @@ def resolve_master
604607
def resolve_slave
605608
sentinel_detect do |client|
606609
if reply = client.call(["sentinel", "slaves", @master])
607-
slave = Hash[*reply.sample]
608-
609-
{:host => slave.fetch("ip"), :port => slave.fetch("port")}
610+
slaves = reply.map { |s| s.each_slice(2).to_h }
611+
slaves.each { |s| s['flags'] = s.fetch('flags').split(',') }
612+
slaves.reject! { |s| s.fetch('flags').include?('s_down') }
613+
614+
if slaves.empty?
615+
raise CannotConnectError, 'No slaves available.'
616+
else
617+
slave = slaves.sample
618+
{
619+
host: slave.fetch('ip'),
620+
port: slave.fetch('port'),
621+
}
622+
end
610623
end
611624
end
612625
end

test/sentinel_test.rb

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,36 @@ def test_sentinel_slave_role_connection
2020
assert_equal MASTER_PORT.to_i, actual[2]
2121
end
2222

23+
def test_the_client_can_connect_to_available_slaves
24+
commands = {
25+
sentinel: lambda do |*_|
26+
[
27+
['ip', '127.0.0.1', 'port', '6382', 'flags', 'slave'],
28+
['ip', '127.0.0.1', 'port', '6383', 'flags', 's_down,slave,disconnected']
29+
]
30+
end
31+
}
32+
RedisMock.start(commands) do |port|
33+
redis = build_slave_role_client(sentinels: [{ host: 'localhost', port: port }])
34+
assert_equal 'PONG', redis.ping
35+
end
36+
end
37+
38+
def test_the_client_raises_error_when_there_is_no_available_slaves
39+
commands = {
40+
sentinel: lambda do |*_|
41+
[
42+
['ip', '127.0.0.1', 'port', '6382', 'flags', 's_down,slave,disconnected'],
43+
['ip', '127.0.0.1', 'port', '6383', 'flags', 's_down,slave,disconnected']
44+
]
45+
end
46+
}
47+
RedisMock.start(commands) do |port|
48+
redis = build_slave_role_client(sentinels: [{ host: 'localhost', port: port }])
49+
assert_raise(Redis::CannotConnectError) { redis.ping }
50+
end
51+
end
52+
2353
def test_sentinel_failover
2454
sentinels = [{:host => "127.0.0.1", :port => 26381},
2555
{:host => "127.0.0.1", :port => 26382}]

0 commit comments

Comments
 (0)