httpx/test/support/session_with_pool.rb
HoneyryderChuck 8bd4dc1fbd fix timers overhead causing spurious wakeups on the select loop
the change to read/write cancellation-driven timeouts as the default
timeout strategy revealed a performance regression; because these were
built on Timers, which never got unsubscribed, this meant that they were
kept beyond the duration of the request they were created for, and
needlessly got picked up for the next timeout tick.

This was fixed by adding a callback on timer intervals, which
unsubscribes them from the timer group when called; these would then be
activated after the timeout is not needed anymore (request send /
response received), thereby removing the overhead on subsequent
requests.

An additional intervals array is also kept in the connection itself;
timeouts from timers are signalled via socket wait calls, however they
were always resulting in timeouts, even when they shouldn't (ex: expect
timeout and send full response payload as a result), and with the wrong
exception class in some cases. By keeping intervals from its requests
around, and monitoring whether there are relevant request triggers, the
connection can therefore handle a timeout or bail out (so that timers
can fire the correct callback).
2023-10-24 22:53:22 +01:00

63 lines
1.3 KiB
Ruby

# frozen_string_literal: true
module SessionWithPool
ConnectionPool = Class.new(HTTPX::Pool) do
attr_reader :resolver, :connections, :selector
attr_reader :connection_count
attr_reader :ping_count
attr_reader :timers
attr_reader :conn_store
def initialize(*)
super
@connection_count = 0
@ping_count = 0
@conn_store = []
def @timers.intervals
@intervals
end
end
def init_connection(connection, _)
super
connection.on(:open) { @connection_count += 1 }
connection.on(:pong) { @ping_count += 1 }
@conn_store << connection
end
def selectable_count
@selector.instance_variable_get(:@selectables).size
end
def find_resolver_for(*args, &blk)
@resolver = super(*args, &blk)
@resolver
end
end
module InstanceMethods
attr_reader :connection_exausted
def pool
@pool ||= ConnectionPool.new
end
def set_connection_callbacks(connection, connections, options)
super
connection.on(:exhausted) do
@connection_exausted = true
end
end
end
module ConnectionMethods
attr_reader :origins
def set_parser_callbacks(parser)
super
parser.on(:pong) { emit(:pong) }
end
end
end