Skip to content

Commit

Permalink
feat: I/O timeouts
Browse files Browse the repository at this point in the history
  • Loading branch information
nickamorim committed Feb 3, 2025
1 parent 2200124 commit ec16c58
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 5 deletions.
21 changes: 16 additions & 5 deletions lib/dalli/socket.rb
Original file line number Diff line number Diff line change
Expand Up @@ -124,12 +124,16 @@ def self.init_socket_options(sock, options)

return unless options[:socket_timeout]

seconds, fractional = options[:socket_timeout].divmod(1)
microseconds = fractional * 1_000_000
timeval = [seconds, microseconds].pack('l_2')
if sock.respond_to?(:timeout=)
sock.timeout = options[:socket_timeout]
else
seconds, fractional = options[:socket_timeout].divmod(1)
microseconds = fractional * 1_000_000
timeval = [seconds, microseconds].pack('l_2')

sock.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_RCVTIMEO, timeval)
sock.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_SNDTIMEO, timeval)
sock.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_RCVTIMEO, timeval)
sock.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_SNDTIMEO, timeval)
end
end

def self.wrapping_ssl_socket(tcp_socket, host, ssl_context)
Expand Down Expand Up @@ -168,9 +172,16 @@ def self.open(path, options = {})
Timeout.timeout(options[:socket_timeout]) do
sock = new(path)
sock.options = { path: path }.merge(options)
init_socket_options(sock, options)
sock
end
end

def self.init_socket_options(sock, options)
# https://man7.org/linux/man-pages/man7/unix.7.html
sock.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_SNDBUF, options[:sndbuf]) if options[:sndbuf]
sock.timeout = options[:socket_timeout] if options[:socket_timeout] && sock.respond_to?(:timeout=)
end
end
end
end
Expand Down
18 changes: 18 additions & 0 deletions test/integration/test_network.rb
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,24 @@
end
end

it 'handles operation timeouts' do
next if p == :binary

memcached_mock(lambda { |sock|
# handle initial version call
sock.gets
sock.write("VERSION 1.6.0\r\n")

sleep(0.3)
}) do
dc = Dalli::Client.new('localhost:19123', socket_timeout: 0.1, protocol: p, socket_max_failures: 0,
socket_failure_delay: 0.0, down_retry_delay: 0.0)
assert_raises Dalli::RingError, message: 'No server available' do
dc.get('abc')
end
end
end

it 'opens a standard TCP connection when ssl_context is not configured' do
memcached_persistent(p) do |dc|
server = dc.send(:ring).servers.first
Expand Down

0 comments on commit ec16c58

Please sign in to comment.