Skip to content

Commit 74cc207

Browse files
committed
redis::client: Add exponential backoff on reconnects
`:reconnect_delay` option can be used alongside `:reconnect_attempts` option to provide a try->fail->retry mechanism for connecting to Redis with exponentinal backoff[1]. By using `:reconnect_delay` option we can avoid having busy-loops that try to reconnect to Redis. `:reconnect_delay_max` adds a time limit to reconnect retries. [1]: https://en.wikipedia.org/wiki/Exponential_backoff
1 parent ec453eb commit 74cc207

File tree

2 files changed

+21
-0
lines changed

2 files changed

+21
-0
lines changed

lib/redis/client.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ class Client
1818
:id => nil,
1919
:tcp_keepalive => 0,
2020
:reconnect_attempts => 1,
21+
:reconnect_delay => 0,
22+
:reconnect_delay_max => 0.5,
2123
:inherit_socket => false
2224
}
2325

@@ -375,6 +377,10 @@ def ensure_connected
375377
disconnect
376378

377379
if attempts <= @options[:reconnect_attempts] && @reconnect
380+
sleep_t = [(@options[:reconnect_delay] * 2**(attempts-1)),
381+
@options[:reconnect_delay_max]].min
382+
383+
Kernel.sleep(sleep_t)
378384
retry
379385
else
380386
raise
@@ -452,6 +458,8 @@ def _parse_options(options)
452458
options[:write_timeout] = Float(options[:write_timeout])
453459

454460
options[:reconnect_attempts] = options[:reconnect_attempts].to_i
461+
options[:reconnect_delay] = options[:reconnect_delay].to_f
462+
options[:reconnect_delay_max] = options[:reconnect_delay_max].to_f
455463

456464
options[:db] = options[:db].to_i
457465
options[:driver] = _parse_driver(options[:driver]) || Connection.drivers.last

test/internals_test.rb

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,19 @@ def test_retry_with_custom_reconnect_attempts_can_still_fail
202202
end
203203
end
204204

205+
def test_retry_with_custom_reconnect_attempts_and_exponential_backoff
206+
close_on_ping([0, 1, 2], :reconnect_attempts => 3,
207+
:reconnect_delay_max => 0.5,
208+
:reconnect_delay => 0.01) do |redis|
209+
210+
Kernel.expects(:sleep).with(0.01).returns(true)
211+
Kernel.expects(:sleep).with(0.02).returns(true)
212+
Kernel.expects(:sleep).with(0.04).returns(true)
213+
214+
assert_equal "3", redis.ping
215+
end
216+
end
217+
205218
def test_don_t_retry_when_second_read_in_pipeline_raises_econnreset
206219
close_on_ping([1]) do |redis|
207220
assert_raise Redis::ConnectionError do

0 commit comments

Comments
 (0)