class HTTP::Retriable::Performer
Request
performing watchdog. @api private
Constants
- RETRIABLE_ERRORS
-
Exceptions we should retry
Public Class Methods
Source
# File lib/http/retriable/performer.rb, line 34 def initialize(opts) @exception_classes = opts.fetch(:exceptions, RETRIABLE_ERRORS) @retry_statuses = opts[:retry_statuses] @tries = opts.fetch(:tries, 5).to_i @on_retry = opts.fetch(:on_retry, ->(*) {}) @should_retry_proc = opts[:should_retry] @delay_calculator = DelayCalculator.new(opts) end
@param [Hash] opts @option opts [#to_i] :tries (5) @option opts [#call, to_i] :delay (DELAY_PROC) @option opts [Array(Exception)] :exceptions (RETRIABLE_ERRORS
) @option opts [Array(to_i)] :retry_statuses @option opts [#call] :on_retry @option opts [#to_f] :max_delay (Float::MAX) @option opts [#call] :should_retry
Public Instance Methods
Source
# File lib/http/retriable/performer.rb, line 76 def calculate_delay(iteration, response) @delay_calculator.call(iteration, response) end
Source
# File lib/http/retriable/performer.rb, line 51 def perform(client, req, &block) 1.upto(Float::INFINITY) do |attempt| # infinite loop with index err, res = try_request(&block) if retry_request?(req, err, res, attempt) begin wait_for_retry_or_raise(req, err, res, attempt) ensure # Some servers support Keep-Alive on any response. Thus we should # flush response before retry, to avoid state error (when socket # has pending response data and we try to write new request). # Alternatively, as we don't need response body here at all, we # are going to close client, effectivle closing underlying socket # and resetting client's state. client.close end elsif err client.close raise err elsif res return res end end end
Watches request/response execution.
If any of {RETRIABLE_ERRORS} occur or response status is ‘5xx`, retries up to `:tries` amount of times. Sleeps for amount of seconds calculated with `:delay` proc before each retry.
@see initialize @api private
Private Instance Methods
Source
# File lib/http/retriable/performer.rb, line 140 def out_of_retries_error(request, response, exception) message = "#{request.verb.to_s.upcase} <#{request.uri}> failed" message += " with #{response.status}" if response message += ":#{exception}" if exception HTTP::OutOfRetriesError.new(message).tap do |ex| ex.cause = exception ex.response = response end end
Builds OutOfRetriesError
@param request [HTTP::Request] @param status [HTTP::Response, nil] @param exception [Exception, nil]
Source
# File lib/http/retriable/performer.rb, line 106 def retry_exception?(err) @exception_classes.any? { |e| err.is_a?(e) } end
Source
# File lib/http/retriable/performer.rb, line 96 def retry_request?(req, err, res, attempt) if @should_retry_proc @should_retry_proc.call(req, err, res, attempt) elsif err retry_exception?(err) else retry_response?(res) end end
rubocop:enable Lint/RescueException
Source
# File lib/http/retriable/performer.rb, line 110 def retry_response?(res) return false unless @retry_statuses response_status = res.status.to_i retry_matchers = [@retry_statuses].flatten retry_matchers.any? do |matcher| case matcher when Range then matcher.cover?(response_status) when Numeric then matcher == response_status else matcher.call(response_status) end end end
Source
# File lib/http/retriable/performer.rb, line 83 def try_request err, res = nil begin res = yield rescue Exception => e err = e end [err, res] end
rubocop:disable Lint/RescueException
Source
# File lib/http/retriable/performer.rb, line 125 def wait_for_retry_or_raise(req, err, res, attempt) if attempt < @tries @on_retry.call(req, err, res) sleep calculate_delay(attempt, res) else res&.flush raise out_of_retries_error(req, res, err) end end