Skip to content

Commit 8113439

Browse files
committed
Migrate sentinel code to redis-client
1 parent ca491bc commit 8113439

File tree

8 files changed

+59
-65
lines changed

8 files changed

+59
-65
lines changed

.github/workflows/test.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ jobs:
179179
path: tmp
180180
key: "local-tmp-redis-7.0-on-ubuntu-latest"
181181
- name: Booting up Redis
182-
run: make start_all
182+
run: make start_sentinel wait_for_sentinel
183183
- name: Test
184184
run: bundle exec rake test:sentinel
185185
- name: Shutting down Redis
@@ -217,7 +217,7 @@ jobs:
217217
path: tmp
218218
key: "local-tmp-redis-7.0-on-ubuntu-latest"
219219
- name: Booting up Redis
220-
run: make start_all
220+
run: make start_cluster
221221
- name: Test
222222
run: bundle exec rake test:cluster
223223
- name: Shutting down Redis

Gemfile

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,4 @@ gem 'minitest'
88
gem 'rake'
99
gem 'rubocop', '~> 1.25.1'
1010
gem 'mocha'
11-
gem 'hiredis'
1211
gem 'redis-client', github: 'redis-rb/redis-client' # RESP2 supportnot yet released

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ If you want to [authenticate](https://redis.io/topics/sentinel#configuring-senti
111111
SENTINELS = [{ host: '127.0.0.1', port: 26380, password: 'mysecret' },
112112
{ host: '127.0.0.1', port: 26381, password: 'mysecret' }]
113113

114-
redis = Redis.new(host: 'mymaster', sentinels: SENTINELS, role: :master)
114+
redis = Redis.new(name: 'mymaster', sentinels: SENTINELS, role: :master)
115115
```
116116

117117
## Cluster support

bin/cluster_creator

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
#!/usr/bin/env ruby
2-
32
# frozen_string_literal: true
43

4+
require 'bundler/setup'
5+
56
$LOAD_PATH.unshift(File.expand_path('../lib', __dir__))
67
require_relative '../test/support/cluster/orchestrator'
78

lib/redis.rb

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ def deprecate!(message)
4646
# @option options [Integer, Array<Integer, Float>] :reconnect_attempts Number of attempts trying to connect,
4747
# or a list of sleep duration between attempts.
4848
# @option options [Boolean] :inherit_socket (false) Whether to use socket in forked process or not
49+
# @option options [String] :name The name of the server group to connect to.
4950
# @option options [Array] :sentinels List of sentinels to contact
5051
# @option options [Symbol] :role (:master) Role to fetch via Sentinel, either `:master` or `:slave`
5152
# @option options [Array<String, Hash{Symbol => String, Integer}>] :cluster List of cluster nodes to contact
@@ -56,19 +57,38 @@ def deprecate!(message)
5657
#
5758
# @return [Redis] a new client instance
5859
def initialize(options = {})
60+
@monitor = Monitor.new
5961
@options = options.dup
6062
@options[:reconnect_attempts] = 1 unless @options.key?(:reconnect_attempts)
6163
if ENV["REDIS_URL"] && SERVER_URL_OPTIONS.none? { |o| @options.key?(o) }
6264
@options[:url] = ENV["REDIS_URL"]
6365
end
6466
inherit_socket = @options.delete(:inherit_socket)
65-
@cluster_mode = options.key?(:cluster)
66-
client = @cluster_mode ? Cluster : Client
6767
@subscription_client = nil
68-
@client = client.new(@options)
68+
69+
@client = if @cluster_mode = options.key?(:cluster)
70+
Cluster.new(@options)
71+
elsif @options.key?(:sentinels)
72+
if url = @options.delete(:url)
73+
uri = URI.parse(url)
74+
if !@options.key?(:name) && uri.host
75+
@options[:name] = uri.host
76+
end
77+
78+
if !@options.key?(:password) && uri.password && !uri.password.empty?
79+
@options[:password] = uri.password
80+
end
81+
82+
if !@options.key?(:username) && uri.user && !uri.user.empty?
83+
@options[:username] = uri.user
84+
end
85+
end
86+
87+
Client.sentinel(**@options).new_client
88+
else
89+
Client.new(@options)
90+
end
6991
@client.inherit_socket! if inherit_socket
70-
@queue = Hash.new { |h, k| h[k] = [] }
71-
@monitor = Monitor.new
7292
end
7393

7494
# Run code without the client reconnecting

lib/redis/client.rb

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,19 @@ class Client < ::RedisClient
1010
RedisClient::ReadTimeoutError => Redis::TimeoutError,
1111
RedisClient::CannotConnectError => Redis::CannotConnectError,
1212
RedisClient::AuthenticationError => Redis::CannotConnectError,
13+
RedisClient::FailoverError => Redis::CannotConnectError,
1314
RedisClient::PermissionError => Redis::PermissionError,
1415
RedisClient::WrongTypeError => Redis::WrongTypeError,
1516
RedisClient::RESP3::UnknownType => Redis::ProtocolError,
1617
}.freeze
1718

1819
class << self
1920
def config(**kwargs)
20-
::RedisClient.config(protocol: 2, **kwargs)
21+
super(protocol: 2, **kwargs)
22+
end
23+
24+
def sentinel(**kwargs)
25+
super(protocol: 2, **kwargs)
2126
end
2227
end
2328

makefile

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,19 +54,19 @@ start: ${BINARY}
5454
stop_slave:
5555
@$(call kill-redis,${SLAVE_PID_PATH})
5656

57-
start_slave: ${BINARY}
57+
start_slave: start
5858
@${BINARY}\
5959
--daemonize yes\
6060
--pidfile ${SLAVE_PID_PATH}\
6161
--port ${SLAVE_PORT}\
6262
--unixsocket ${SLAVE_SOCKET_PATH}\
6363
--slaveof 127.0.0.1 ${PORT}
6464

65-
stop_sentinel:
65+
stop_sentinel: stop_slave stop
6666
@$(call kill-redis,${SENTINEL_PID_PATHS})
6767
@rm -f ${TMP}/sentinel*.conf || true
6868

69-
start_sentinel: ${BINARY}
69+
start_sentinel: start start_slave
7070
@for port in ${SENTINEL_PORTS}; do\
7171
conf=${TMP}/sentinel$$port.conf;\
7272
touch $$conf;\

test/sentinel/sentinel_test.rb

Lines changed: 20 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -124,17 +124,17 @@ def test_sentinel_failover_prioritize_healthy_sentinel
124124
end
125125
end
126126

127-
assert_equal commands[:s1], [%w[get-master-addr-by-name master1]]
128-
assert_equal commands[:s2], [%w[get-master-addr-by-name master1], %w[get-master-addr-by-name master1]]
127+
assert_equal [%w[get-master-addr-by-name master1]], commands[:s1]
128+
assert_equal [%w[get-master-addr-by-name master1]], commands[:s2]
129129
end
130130

131131
def test_sentinel_with_non_sentinel_options
132132
commands = { s1: [], m1: [] }
133133

134134
sentinel = lambda do |port|
135135
{
136-
auth: lambda do |pass|
137-
commands[:s1] << ['auth', pass]
136+
auth: lambda do |*args|
137+
commands[:s1] << ['auth', *args]
138138
'+OK'
139139
end,
140140
select: lambda do |db|
@@ -149,8 +149,8 @@ def test_sentinel_with_non_sentinel_options
149149
end
150150

151151
master = {
152-
auth: lambda do |pass|
153-
commands[:m1] << ['auth', pass]
152+
auth: lambda do |*args|
153+
commands[:m1] << ['auth', *args]
154154
'+OK'
155155
end,
156156
role: lambda do
@@ -176,8 +176,8 @@ def test_authentication_for_sentinel
176176

177177
sentinel = lambda do |port|
178178
{
179-
auth: lambda do |pass|
180-
commands[:s1] << ['auth', pass]
179+
auth: lambda do |*args|
180+
commands[:s1] << ['auth', *args]
181181
'+OK'
182182
end,
183183
select: lambda do |db|
@@ -192,8 +192,8 @@ def test_authentication_for_sentinel
192192
end
193193

194194
master = {
195-
auth: lambda do |pass|
196-
commands[:m1] << ['auth', pass]
195+
auth: lambda do |*args|
196+
commands[:s1] << ['auth', *args]
197197
'-ERR Client sent AUTH, but no password is set'
198198
end,
199199
role: lambda do
@@ -205,7 +205,7 @@ def test_authentication_for_sentinel
205205
RedisMock.start(master) do |master_port|
206206
RedisMock.start(sentinel.call(master_port)) do |sen_port|
207207
s = [{ host: '127.0.0.1', port: sen_port, password: 'foo' }]
208-
r = Redis.new(host: 'master1', sentinels: s, role: :master)
208+
r = Redis.new(name: 'master1', sentinels: s, role: :master)
209209
assert r.ping
210210
end
211211
end
@@ -219,8 +219,8 @@ def test_authentication_for_sentinel_and_redis
219219

220220
sentinel = lambda do |port|
221221
{
222-
auth: lambda do |pass|
223-
commands[:s1] << ['auth', pass]
222+
auth: lambda do |*args|
223+
commands[:s1] << ['auth', *args]
224224
'+OK'
225225
end,
226226
select: lambda do |db|
@@ -235,8 +235,8 @@ def test_authentication_for_sentinel_and_redis
235235
end
236236

237237
master = {
238-
auth: lambda do |pass|
239-
commands[:m1] << ['auth', pass]
238+
auth: lambda do |*args|
239+
commands[:m1] << ['auth', *args]
240240
'+OK'
241241
end,
242242
role: lambda do
@@ -248,7 +248,7 @@ def test_authentication_for_sentinel_and_redis
248248
RedisMock.start(master) do |master_port|
249249
RedisMock.start(sentinel.call(master_port)) do |sen_port|
250250
s = [{ host: '127.0.0.1', port: sen_port, password: 'foo' }]
251-
r = Redis.new(host: 'master1', sentinels: s, role: :master, password: 'bar')
251+
r = Redis.new(name: 'master1', sentinels: s, role: :master, password: 'bar')
252252
assert r.ping
253253
end
254254
end
@@ -291,7 +291,7 @@ def test_authentication_with_acl
291291
RedisMock.start(master) do |master_port|
292292
RedisMock.start(sentinel.call(master_port)) do |sen_port|
293293
s = [{ host: '127.0.0.1', port: sen_port, username: 'bob', password: 'foo' }]
294-
r = Redis.new(host: 'master1', sentinels: s, role: :master, username: 'alice', password: 'bar')
294+
r = Redis.new(name: 'master1', sentinels: s, role: :master, username: 'alice', password: 'bar')
295295
assert r.ping
296296
end
297297
end
@@ -317,18 +317,18 @@ def test_sentinel_role_mismatch
317317
end
318318
}
319319

320-
ex = assert_raises(Redis::ConnectionError) do
320+
ex = assert_raises(Redis::CannotConnectError) do
321321
RedisMock.start(master) do |master_port|
322322
RedisMock.start(sentinel.call(master_port)) do |sen_port|
323323
sentinels[0][:port] = sen_port
324-
redis = Redis.new(url: "redis://master1", sentinels: sentinels, role: :master)
324+
redis = Redis.new(url: "redis://master1", sentinels: sentinels, role: :master, reconnect_attempts: 0)
325325

326326
assert redis.ping
327327
end
328328
end
329329
end
330330

331-
assert_match(/Instance role mismatch/, ex.message)
331+
assert_match(/Expected to connect to a master, but the server is a replica/, ex.message)
332332
end
333333

334334
def test_sentinel_retries
@@ -389,35 +389,4 @@ def test_sentinel_retries
389389

390390
assert_match(/No sentinels available/, ex.message)
391391
end
392-
393-
def test_sentinel_with_string_option_keys
394-
commands = []
395-
396-
master = {
397-
role: lambda do
398-
['master']
399-
end
400-
}
401-
402-
sentinel = lambda do |port|
403-
{
404-
sentinel: lambda do |command, *args|
405-
commands << [command, *args]
406-
['127.0.0.1', port.to_s]
407-
end
408-
}
409-
end
410-
411-
RedisMock.start(master) do |master_port|
412-
RedisMock.start(sentinel.call(master_port)) do |sen_port|
413-
sentinels = [{ 'host' => '127.0.0.1', 'port' => sen_port }]
414-
415-
redis = Redis.new(url: 'redis://master1', 'sentinels' => sentinels, 'role' => :master)
416-
417-
assert redis.ping
418-
end
419-
end
420-
421-
assert_equal [%w[get-master-addr-by-name master1]], commands
422-
end
423392
end

0 commit comments

Comments
 (0)