fixing selector timeout errors closing all connections and ignoring

resolvers

All kinds of errors happening during the select loop, will be handled as
abrupt select loop errors, and terminate all connections; this also
includes timmeout errors. This is not ideal, for some reasons:
connection timeout errors happening on the loop close all connections,
although it may be only triggered for one (or a subset of) connection
for which the timeout should trigger; second, errors on the DS channel
propagate errors to connections indirectly (the emission mentioned
above), wrongly (connections for different hostnames not yet queried,
will also fail with timeout), and won't clean the resolver state (so
subsequent queries will be done for the same hostname which failed in
the first place).

This fix is a first step to solving this problem. It does not totally
address the first, but i'll fix dealing with errors from the second
use-case.
This commit is contained in:
HoneyryderChuck 2022-06-22 01:55:16 +03:00
parent d8e5894979
commit 6c911768fe
5 changed files with 27 additions and 5 deletions

View File

@ -276,6 +276,12 @@ module HTTPX
@state == :open || @state == :inactive
end
def raise_timeout_error(interval)
error = HTTPX::TimeoutError.new(interval, "timed out while waiting on select")
error.set_backtrace(caller)
on_error(error)
end
private
def connect

View File

@ -118,6 +118,10 @@ module HTTPX
@timeouts.values_at(*hosts).reject(&:empty?).map(&:first).min
end
def raise_timeout_error(interval)
do_retry(interval)
end
private
def calculate_interests
@ -134,10 +138,10 @@ module HTTPX
dwrite if calculate_interests == :w
end
def do_retry
def do_retry(loop_time = nil)
return if @queries.empty? || !@start_timeout
loop_time = Utils.elapsed_time(@start_timeout)
loop_time ||= Utils.elapsed_time(@start_timeout)
query = @queries.first

View File

@ -74,7 +74,10 @@ class HTTPX::Selector
readers, writers = IO.select(r, w, nil, interval)
raise HTTPX::TimeoutError.new(interval, "timed out while waiting on select") if readers.nil? && writers.nil? && interval
if readers.nil? && writers.nil? && interval
[*r, *w].each { |io| io.raise_timeout_error(interval) }
return
end
rescue IOError, SystemCallError
@selectables.reject!(&:closed?)
retry
@ -108,7 +111,11 @@ class HTTPX::Selector
when nil then return
end
raise HTTPX::TimeoutError.new(interval, "timed out while waiting on select") unless result || interval.nil?
unless result || interval.nil?
io.raise_timeout_error(interval)
return
end
# raise HTTPX::TimeoutError.new(interval, "timed out while waiting on select")
yield io
rescue IOError, SystemCallError

View File

@ -72,6 +72,9 @@ module HTTPX
def timeout: () -> Numeric?
def deactivate: () -> void
def raise_timeout_error: (Numeric interval) -> void
private
def initialize: (String, URI::Generic, options) -> untyped

View File

@ -27,6 +27,8 @@ module HTTPX
def timeout: () -> Numeric?
def raise_timeout_error: (Numeric interval) -> void
private
def initialize: (ip_family family, options options) -> void
@ -35,7 +37,7 @@ module HTTPX
def consume: () -> void
def do_retry: () -> void
def do_retry: (?Numeric loop_time) -> void
def dread: (Integer) -> void
| () -> void