From 4acae51c42c1aba57ca3a7c4b33dec4bbc5a8ff7 Mon Sep 17 00:00:00 2001 From: Stefan Kaes Date: Thu, 20 Jul 2023 09:01:30 +0200 Subject: [PATCH] rescue IO::TimeoutError raised by Ruby since 3.2.0 on blocking reads/writes One scenario in which this happens is when a firewall silently drops connections between clients and memcached instances. With this patch the client reopens the connection and the request succeeds. Without it, IO:TimeoutError is raised for the application to handle which deviates from Dalli behavior under Ruby 3.1 and earlier. --- lib/dalli/protocol.rb | 9 +++++++++ lib/dalli/protocol/base.rb | 2 +- lib/dalli/protocol/connection_manager.rb | 6 +++--- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/lib/dalli/protocol.rb b/lib/dalli/protocol.rb index 1a7bac17..ef7983b1 100644 --- a/lib/dalli/protocol.rb +++ b/lib/dalli/protocol.rb @@ -4,5 +4,14 @@ module Dalli module Protocol # Preserved for backwards compatibility. Should be removed in 4.0 NOT_FOUND = ::Dalli::NOT_FOUND + + # Ruby 3.2 raises IO::TimeoutError on blocking reads/writes, but + # it is not defined in earlier Ruby versions. + require 'timeout' + if defined?(IO::TimeoutError) + TIMEOUT_ERRORS = [Timeout::Error, IO::TimeoutError] + else + TIMEOUT_ERRORS = [Timeout::Error] + end end end diff --git a/lib/dalli/protocol/base.rb b/lib/dalli/protocol/base.rb index f5cffb6c..74274f80 100644 --- a/lib/dalli/protocol/base.rb +++ b/lib/dalli/protocol/base.rb @@ -106,7 +106,7 @@ def pipeline_next_responses end values - rescue SystemCallError, Timeout::Error, EOFError => e + rescue SystemCallError, *TIMEOUT_ERRORS, EOFError => e @connection_manager.error_on_request!(e) end diff --git a/lib/dalli/protocol/connection_manager.rb b/lib/dalli/protocol/connection_manager.rb index 0f5a01b6..27b514ce 100644 --- a/lib/dalli/protocol/connection_manager.rb +++ b/lib/dalli/protocol/connection_manager.rb @@ -150,19 +150,19 @@ def read_line data = @sock.gets("\r\n") error_on_request!('EOF in read_line') if data.nil? data - rescue SystemCallError, Timeout::Error, EOFError => e + rescue SystemCallError, *TIMEOUT_ERRORS, EOFError => e error_on_request!(e) end def read(count) @sock.readfull(count) - rescue SystemCallError, Timeout::Error, EOFError => e + rescue SystemCallError, *TIMEOUT_ERRORS, EOFError => e error_on_request!(e) end def write(bytes) @sock.write(bytes) - rescue SystemCallError, Timeout::Error => e + rescue SystemCallError, *TIMEOUT_ERRORS => e error_on_request!(e) end