Skip to content

Commit

Permalink
Use the exception: false API of read/write/connect nonblock
Browse files Browse the repository at this point in the history
  • Loading branch information
byroot committed Sep 27, 2020
1 parent eac9b35 commit ff1ef3f
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 50 deletions.
3 changes: 3 additions & 0 deletions .rubocop.yml
Expand Up @@ -57,6 +57,9 @@ Style/ParallelAssignment:
Style/NumericPredicate:
Enabled: false

Style/IfUnlessModifier:
Enabled: false

Style/SignalException:
Exclude:
- 'lib/redis/connection/synchrony.rb'
Expand Down
104 changes: 55 additions & 49 deletions lib/redis/connection/ruby.rb
Expand Up @@ -49,43 +49,47 @@ def gets
end

def _read_from_socket(nbytes)
begin
read_nonblock(nbytes)
rescue IO::WaitReadable
if IO.select([self], nil, nil, @timeout)
retry
else
raise Redis::TimeoutError
end
rescue IO::WaitWritable
if IO.select(nil, [self], nil, @timeout)
retry
else
raise Redis::TimeoutError
loop do
case chunk = read_nonblock(nbytes, exception: false)
when :wait_readable
unless IO.select([self], nil, nil, @timeout)
raise Redis::TimeoutError
end
when :wait_writable
unless IO.select(nil, [self], nil, @timeout)
raise Redis::TimeoutError
end
when nil
raise Errno::ECONNRESET
when String
return chunk
end
end
rescue EOFError
raise Errno::ECONNRESET
end

def _write_to_socket(data)
begin
write_nonblock(data)
rescue IO::WaitWritable
if IO.select(nil, [self], nil, @write_timeout)
retry
else
raise Redis::TimeoutError
end
rescue IO::WaitReadable
if IO.select([self], nil, nil, @write_timeout)
retry
else
raise Redis::TimeoutError
total_bytes_written = 0
loop do
case bytes_written = write_nonblock(data, exception: false)
when :wait_readable
unless IO.select([self], nil, nil, @timeout)
raise Redis::TimeoutError
end
when :wait_writable
unless IO.select(nil, [self], nil, @timeout)
raise Redis::TimeoutError
end
when nil
raise Errno::ECONNRESET
when Integer
total_bytes_written += bytes_written
if bytes_written < data.bytesize
data.slice!(0, bytes_written)
else
return total_bytes_written
end
end
end
rescue EOFError
raise Errno::ECONNRESET
end

def write(data)
Expand Down Expand Up @@ -245,25 +249,27 @@ def self.connect(host, port, timeout, ssl_params)
ssl_sock = new(tcp_sock, ctx)
ssl_sock.hostname = host

begin
# Initiate the socket connection in the background. If it doesn't fail
# immediately it will raise an IO::WaitWritable (Errno::EINPROGRESS)
# indicating the connection is in progress.
# Unlike waiting for a tcp socket to connect, you can't time out ssl socket
# connections during the connect phase properly, because IO.select only partially works.
# Instead, you have to retry.
ssl_sock.connect_nonblock
rescue Errno::EAGAIN, Errno::EWOULDBLOCK, IO::WaitReadable
if IO.select([ssl_sock], nil, nil, timeout)
retry
else
raise TimeoutError
end
rescue IO::WaitWritable
if IO.select(nil, [ssl_sock], nil, timeout)
retry
else
raise TimeoutError
loop do
begin
# Initiate the socket connection in the background. If it doesn't fail
# immediately it will raise an IO::WaitWritable (Errno::EINPROGRESS)
# indicating the connection is in progress.
# Unlike waiting for a tcp socket to connect, you can't time out ssl socket
# connections during the connect phase properly, because IO.select only partially works.
# Instead, you have to retry.
ssl_sock.connect_nonblock
rescue Errno::EAGAIN, Errno::EWOULDBLOCK, IO::WaitReadable
if IO.select([ssl_sock], nil, nil, timeout)
retry
else
raise TimeoutError
end
rescue IO::WaitWritable
if IO.select(nil, [ssl_sock], nil, timeout)
retry
else
raise TimeoutError
end
end
end

Expand Down
2 changes: 1 addition & 1 deletion test/internals_test.rb
Expand Up @@ -244,7 +244,7 @@ def close_on_connection(seq)
else
raise "Expected SELECT"
end
unless seq.include?(n) # rubocop:disable Style/IfUnlessModifier
unless seq.include?(n)
session.write("+#{n}\r\n") while read_command.call(session)
end
end
Expand Down

0 comments on commit ff1ef3f

Please sign in to comment.