Class: HTTP::Retriable::Performer Private
- Inherits:
-
Object
- Object
- HTTP::Retriable::Performer
- Defined in:
- lib/http/retriable/performer.rb,
sig/http.rbs
Overview
This class is part of a private API. You should avoid using this class if possible, as it may be removed or be changed in the future.
Request performing watchdog.
Constant Summary collapse
- RETRIABLE_ERRORS =
This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.
Exceptions we should retry
[ HTTP::TimeoutError, HTTP::ConnectionError, IO::EAGAINWaitReadable, Errno::ECONNRESET, Errno::ECONNREFUSED, Errno::EHOSTUNREACH, OpenSSL::SSL::SSLError, EOFError, IOError ].freeze
Instance Method Summary collapse
-
#calculate_delay(iteration, response) ⇒ Numeric
private
Calculates delay between retries.
-
#finish_attempt(client, err) ⇒ void
private
Closes client and raises the error.
-
#initialize(tries: 5, delay: nil, exceptions: RETRIABLE_ERRORS, retry_statuses: nil, on_retry: ->(*_args) {}, max_delay: Float::MAX, should_retry: nil) ⇒ HTTP::Retriable::Performer
constructor
private
Create a new retry performer.
-
#out_of_retries_error(request, response, exception) ⇒ HTTP::OutOfRetriesError
private
Builds OutOfRetriesError.
-
#perform(client, req) { ... } ⇒ HTTP::Response
private
Execute request with retry logic.
-
#retry_attempt(client, req, err, res, attempt) ⇒ void
private
Executes a single retry attempt.
-
#retry_exception?(err) ⇒ Boolean
private
Checks whether the exception is retriable.
-
#retry_request?(req, err, res, attempt) ⇒ Boolean
private
Checks whether the request should be retried.
-
#retry_response?(res) ⇒ Boolean
private
Checks whether the response status warrants retry.
-
#try_request { ... } ⇒ Array
private
Attempts to execute the request block.
-
#wait_for_retry_or_raise(req, err, res, attempt) ⇒ void
private
Waits for retry delay or raises if out of attempts.
Constructor Details
#initialize(tries: 5, delay: nil, exceptions: RETRIABLE_ERRORS, retry_statuses: nil, on_retry: ->(*_args) {}, max_delay: Float::MAX, should_retry: nil) ⇒ HTTP::Retriable::Performer
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Create a new retry performer
38 39 40 41 42 43 44 45 46 |
# File 'lib/http/retriable/performer.rb', line 38 def initialize(tries: 5, delay: nil, exceptions: RETRIABLE_ERRORS, retry_statuses: nil, on_retry: ->(*_args) {}, max_delay: Float::MAX, should_retry: nil) @exception_classes = exceptions @retry_statuses = retry_statuses @tries = tries.to_i @on_retry = on_retry @should_retry_proc = should_retry @delay_calculator = DelayCalculator.new(delay: delay, max_delay: max_delay) end |
Instance Method Details
#calculate_delay(iteration, response) ⇒ Numeric
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Calculates delay between retries
73 74 75 |
# File 'lib/http/retriable/performer.rb', line 73 def calculate_delay(iteration, response) @delay_calculator.call(iteration, response) end |
#finish_attempt(client, err) ⇒ void
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
This method returns an undefined value.
Closes client and raises the error
99 100 101 102 |
# File 'lib/http/retriable/performer.rb', line 99 def finish_attempt(client, err) client.close raise err end |
#out_of_retries_error(request, response, exception) ⇒ HTTP::OutOfRetriesError
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Builds OutOfRetriesError
184 185 186 187 188 189 190 191 192 193 194 |
# File 'lib/http/retriable/performer.rb', line 184 def out_of_retries_error(request, response, exception) = format("%s <%s> failed", String(request.verb).upcase, request.uri) += " with #{response.status}" if response += ":#{exception}" if exception OutOfRetriesError.new().tap do |ex| ex.cause = exception ex.response = response end end |
#perform(client, req) { ... } ⇒ HTTP::Response
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Execute request with retry logic
53 54 55 56 57 58 59 60 61 62 63 64 65 |
# File 'lib/http/retriable/performer.rb', line 53 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) retry_attempt(client, req, err, res, attempt) elsif err finish_attempt(client, err) elsif res return res end end end |
#retry_attempt(client, req, err, res, attempt) ⇒ void
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
This method returns an undefined value.
Executes a single retry attempt
83 84 85 86 87 88 89 90 91 92 93 |
# File 'lib/http/retriable/performer.rb', line 83 def retry_attempt(client, req, err, res, attempt) # 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, effectively closing underlying socket # and resetting client's state. wait_for_retry_or_raise(req, err, res, attempt) ensure client.close end |
#retry_exception?(err) ⇒ Boolean
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Checks whether the exception is retriable
140 141 142 |
# File 'lib/http/retriable/performer.rb', line 140 def retry_exception?(err) @exception_classes.any? { |e| err.is_a?(e) } end |
#retry_request?(req, err, res, attempt) ⇒ Boolean
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Checks whether the request should be retried
126 127 128 129 130 131 132 133 134 |
# File 'lib/http/retriable/performer.rb', line 126 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 |
#retry_response?(res) ⇒ Boolean
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Checks whether the response status warrants retry
148 149 150 151 152 153 154 155 156 157 158 159 160 161 |
# File 'lib/http/retriable/performer.rb', line 148 def retry_response?(res) return false unless @retry_statuses response_status = Integer(res.status) 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 |
#try_request { ... } ⇒ Array
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Attempts to execute the request block
rubocop:disable Lint/RescueException
109 110 111 112 113 114 115 116 117 118 119 |
# File 'lib/http/retriable/performer.rb', line 109 def try_request err, res = nil begin res = yield rescue Exception => e err = e end [err, res] end |
#wait_for_retry_or_raise(req, err, res, attempt) ⇒ void
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
This method returns an undefined value.
Waits for retry delay or raises if out of attempts
167 168 169 170 171 172 173 174 175 |
# File 'lib/http/retriable/performer.rb', line 167 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 |