Skip to content

Commit 8c2218b

Browse files
author
Brian Hahn
committed
add write timeout feature to ruby driver
1 parent 379ba1a commit 8c2218b

File tree

3 files changed

+46
-1
lines changed

3 files changed

+46
-1
lines changed

lib/redis/client.rb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ class Client
1313
:path => nil,
1414
:timeout => 5.0,
1515
:connect_timeout => 5.0,
16+
:write_timeout => nil,
1617
:password => nil,
1718
:db => 0,
1819
:driver => nil,
@@ -419,6 +420,8 @@ def _parse_options(options)
419420
options[:timeout]
420421
end
421422

423+
options[:write_timeout] = options[:write_timeout] ? options[:write_timeout].to_f : options[:timeout]
424+
422425
options[:db] = options[:db].to_i
423426
options[:driver] = _parse_driver(options[:driver]) || Connection.drivers.last
424427

lib/redis/connection/ruby.rb

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ module SocketMixin
1212
def initialize(*args)
1313
super(*args)
1414

15-
@timeout = nil
15+
@timeout = @write_timeout = nil
1616
@buffer = ""
1717
end
1818

@@ -24,6 +24,14 @@ def timeout=(timeout)
2424
end
2525
end
2626

27+
def write_timeout=(timeout)
28+
if timeout && timeout > 0
29+
@write_timeout = timeout
30+
else
31+
@write_timeout = nil
32+
end
33+
end
34+
2735
def read(nbytes)
2836
result = @buffer.slice!(0, nbytes)
2937

@@ -59,6 +67,11 @@ def _read_from_socket(nbytes)
5967
rescue EOFError
6068
raise Errno::ECONNRESET
6169
end
70+
71+
# UNIXSocket and TCPSocket don't support write timeouts
72+
def write(*args)
73+
Timeout.timeout(@write_timeout, TimeoutError) { super }
74+
end
6275
end
6376

6477
if defined?(RUBY_ENGINE) && RUBY_ENGINE == "jruby"
@@ -213,6 +226,7 @@ def self.connect(config)
213226

214227
instance = new(sock)
215228
instance.timeout = config[:timeout]
229+
instance.write_timeout = config[:write_timeout]
216230
instance.set_tcp_keepalive config[:tcp_keepalive]
217231
instance
218232
end
@@ -265,6 +279,10 @@ def timeout=(timeout)
265279
end
266280
end
267281

282+
def write_timeout=(timeout)
283+
@sock.write_timeout = timeout
284+
end
285+
268286
def write(command)
269287
@sock.write(build_command(command))
270288
end

test/internals_test.rb

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,30 @@ def test_connection_timeout
160160
assert (Time.now - start_time) <= opts[:timeout]
161161
end
162162

163+
driver(:ruby) do
164+
def test_write_timeout
165+
redis_mock(:write_timeout => 0.1, :timeout => 1) do |redis|
166+
# This is really disgusting, but this essentially mocks the "super" call in write
167+
IO.class_eval do
168+
alias_method :old_write, :write
169+
170+
def write(*args)
171+
sleep 1
172+
end
173+
end
174+
assert_raise Redis::TimeoutError do
175+
redis.rpush("tmp", "test")
176+
end
177+
end
178+
ensure
179+
IO.class_eval do
180+
undef_method :write
181+
alias_method :write, :old_write
182+
undef_method :old_write
183+
end
184+
end
185+
end
186+
163187
def close_on_ping(seq, options = {})
164188
$request = 0
165189

0 commit comments

Comments
 (0)