Class: Faraday::Retry::Middleware

Inherits:
Middleware
  • Object
show all
Defined in:
lib/faraday/retry/middleware.rb

Overview

This class provides the main implementation for your middleware. Your middleware can implement any of the following methods:

  • on_request - called when the request is being prepared

  • on_complete - called when the response is being processed

Optionally, you can also override the following methods from Faraday::Middleware

  • initialize(app, options = {}) - the initializer method

  • call(env) - the main middleware invocation method. This already calls on_request and on_complete, so you normally don’t need to override it. You may need to in case you need to “wrap” the request or need more control (see “retry” middleware: github.com/lostisland/faraday/blob/main/lib/faraday/request/retry.rb#L142). IMPORTANT: Remember to call ‘@app.call(env)` or `super` to not interrupt the middleware chain!

Defined Under Namespace

Classes: Options

Constant Summary collapse

DEFAULT_EXCEPTIONS =
[
  Errno::ETIMEDOUT, 'Timeout::Error',
  Faraday::TimeoutError, Faraday::RetriableResponse
].freeze
IDEMPOTENT_METHODS =
%i[delete get head options put].freeze

Instance Method Summary collapse

Constructor Details

#initialize(app, options = nil) ⇒ Middleware

Returns a new instance of Middleware.

Parameters:

  • app (#call)
  • options (Hash) (defaults to: nil)

Options Hash (options):

  • :max (Integer) — default: 2

    Maximum number of retries

  • :interval (Integer) — default: 0

    Pause in seconds between retries

  • :interval_randomness (Integer) — default: 0

    The maximum random interval amount expressed as a float between 0 and 1 to use in addition to the interval.

  • :max_interval (Integer) — default: Float::MAX

    An upper limit for the interval

  • :backoff_factor (Integer) — default: 1

    The amount to multiply each successive retry’s interval amount by in order to provide backoff

  • :exceptions (Array) — default: [ Errno::ETIMEDOUT, 'Timeout::Error', Faraday::TimeoutError, Faraday::RetriableResponse]

    The list of exceptions to handle. Exceptions can be given as Class, Module, or String.

  • :methods (Array<Symbol>) — default: the idempotent HTTP methods in IDEMPOTENT_METHODS

    A list of HTTP methods, as symbols, to retry without calling retry_if. Pass an empty Array to call retry_if for all exceptions.

  • :retry_if (Block) — default: false

    block that will receive the env object and the exception raised and should decide if the code should retry still the action or not independent of the retry count. This would be useful if the exception produced is non-recoverable or if the the HTTP method called is not idempotent.

  • :retry_block (Block)

    block that is executed before every retry. The block will be yielded keyword arguments:

    * env [Faraday::Env]: Request environment
    * options [Faraday::Options]: middleware options
    * retry_count [Integer]: how many retries have already occured (starts at 0)
    * exception [Exception]: exception that triggered the retry,
      will be the synthetic `Faraday::RetriableResponse` if the
      retry was triggered by something other than an exception.
    * will_retry_in [Float]: retry_block is called *before* the retry
      delay, actual retry will happen in will_retry_in number of
      seconds.
    
  • :retry_statuses (Array)

    Array of Integer HTTP status codes or a single Integer value that determines whether to raise a Faraday::RetriableResponse exception based on the HTTP status code of an HTTP response.

  • :header_parser_block (Block)

    block that will receive the the value of the retry header and should return the number of seconds to wait before retrying the request. This is useful if the value of the header is not a number of seconds or a RFC 2822 formatted date.



127
128
129
130
131
# File 'lib/faraday/retry/middleware.rb', line 127

def initialize(app, options = nil)
  super(app)
  @options = Options.from(options)
  @errmatch = build_exception_matcher(@options.exceptions)
end

Instance Method Details

#build_exception_matcher(exceptions) ⇒ Module

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.

An exception matcher for the rescue clause can usually be any object that responds to ‘===`, but for Ruby 1.8 it has to be a Class or Module.

Parameters:

  • exceptions (Array)

Returns:

  • (Module)

    an exception matcher



185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
# File 'lib/faraday/retry/middleware.rb', line 185

def build_exception_matcher(exceptions)
  matcher = Module.new
  (
    class << matcher
      self
    end).class_eval do
    define_method(:===) do |error|
      exceptions.any? do |ex|
        if ex.is_a? Module
          error.is_a? ex
        else
          Object.const_defined?(ex.to_s) && error.is_a?(Object.const_get(ex.to_s))
        end
      end
    end
  end
  matcher
end

#calculate_sleep_amount(retries, env) ⇒ Object



133
134
135
136
137
138
139
140
141
142
143
144
# File 'lib/faraday/retry/middleware.rb', line 133

def calculate_sleep_amount(retries, env)
  retry_after = [calculate_retry_after(env), calculate_rate_limit_reset(env)].compact.max
  retry_interval = calculate_retry_interval(retries)

  return if retry_after && retry_after > @options.max_interval

  if retry_after && retry_after >= retry_interval
    retry_after
  else
    retry_interval
  end
end

#call(env) ⇒ Object

Parameters:

  • env (Faraday::Env)


147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
# File 'lib/faraday/retry/middleware.rb', line 147

def call(env)
  retries = @options.max
  request_body = env[:body]
  begin
    # after failure env[:body] is set to the response body
    env[:body] = request_body
    @app.call(env).tap do |resp|
      raise Faraday::RetriableResponse.new(nil, resp) if @options.retry_statuses.include?(resp.status)
    end
  rescue @errmatch => e
    if retries.positive? && retry_request?(env, e)
      retries -= 1
      rewind_files(request_body)
      if (sleep_amount = calculate_sleep_amount(retries + 1, env))
        @options.retry_block.call(
          env: env,
          options: @options,
          retry_count: @options.max - (retries + 1),
          exception: e,
          will_retry_in: sleep_amount
        )
        sleep sleep_amount
        retry
      end
    end

    raise unless e.is_a?(Faraday::RetriableResponse)

    e.response
  end
end