Skip to content

Commit 54de901

Browse files
stanhuSylvester Chin
andcommitted
Close Redis Sentinel connection after resolving role
On our production instances, Redis Sentinel is configured with the default `maxclients` setting of 10,000, while the Redis nodes are configured with 50,000. After upgrading to the `redis` v5 gem, we found that our clients kept hitting `ERR max number of clients reached` errors on Redis Sentinel nodes. In the `redis` v4.80 gem, `client.disconnect` is always called here: https://github.com/redis/redis-rb/blob/v4.8.0/lib/redis/client.rb#L630 This ensured that every time `resolve_master` or `resolve_slave` executed, clients would be disconnected. This commit restores that behavior by closing the client in `RedisClient::SentinelConfig#each_sentinel`. This should not impact performance since resolving the role of master or replica only needs to happen once, and `config` is memoized. To see the effect of this change, run this example script in a Ruby console with one Redis Sentinel on port 26379, and one Redis node on the default port 6379: ```ruby require 'connection_pool' require 'redis' SENTINELS = [{ host: "127.0.0.1", port: 26379 }] pool = ConnectionPool.new(size: 25) { Redis.new(name: "mymaster", sentinels: SENTINELS, role: :master) } 100.times do Thread.new do pool.with { |redis| redis.ping } end end ``` Previously with `redis` v5.1.0, which uses `redis-client`, we see there are 25 connections to Redis Sentinel, and 25 connections to Redis: ```shell $ lsof -p 1992535 | grep "localhost:26379" | wc -l 25 $ lsof -p 1992535 | grep "localhost:redis" | wc -l 25 ``` With this change: ```shell $ lsof -p 1997557 | grep "localhost:26379" | wc -l 0 $ lsof -p 1997557 | grep "localhost:redis" | wc -l 25 ``` Co-authored-by: Sylvester Chin <[email protected]>
1 parent 4b6665a commit 54de901

File tree

2 files changed

+14
-0
lines changed

2 files changed

+14
-0
lines changed

lib/redis_client/sentinel_config.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,10 @@ def each_sentinel
188188
if success
189189
@sentinel_configs.unshift(@sentinel_configs.delete(sentinel_config))
190190
end
191+
# Redis Sentinels may be configured to have a lower maxclients setting than
192+
# the Redis nodes. Close the connection to the Sentinel node to avoid using
193+
# a connection.
194+
sentinel_client.close
191195
end
192196
end
193197

test/sentinel/sentinel_test.rb

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,11 @@ def test_sentinel_all_down
5050
end
5151

5252
class SentinelClientMock
53+
attr_reader :close_count
54+
5355
def initialize(responses)
5456
@responses = responses
57+
@close_count = 0
5558
end
5659

5760
def call(*args)
@@ -66,6 +69,10 @@ def call(*args)
6669
raise "Expected #{command.inspect}, got: #{args.inspect}"
6770
end
6871
end
72+
73+
def close
74+
@close_count += 1
75+
end
6976
end
7077

7178
def test_unknown_master
@@ -184,6 +191,7 @@ def test_successful_connection_refreshes_sentinels_list
184191
assert_equal Servers::SENTINELS.length + 1, @config.sentinels.length
185192
assert_equal new_sentinel_ip, @config.sentinels.last.host
186193
assert_equal new_sentinel_port, @config.sentinels.last.port
194+
assert_equal 1, sentinel_client_mock.close_count
187195
end
188196

189197
def test_sentinel_refresh_password
@@ -211,6 +219,8 @@ def test_sentinel_refresh_password
211219
@config.sentinels.each do |sentinel|
212220
refute_nil sentinel.password
213221
end
222+
223+
assert_equal 1, sentinel_client_mock.close_count
214224
end
215225

216226
def test_config_user_password_from_url_for_redis_master_replica_only

0 commit comments

Comments
 (0)