Class: Faraday::Request::Retry

Inherits:
Middleware show all
Defined in:
lib/faraday/request/retry.rb

Overview

Catches exceptions and retries each request a limited number of times.

By default, it retries 2 times and handles only timeout exceptions. It can be configured with an arbitrary number of retries, a list of exceptions to handle, a retry interval, a percentage of randomness to add to the retry interval, and a backoff factor.

Examples

Faraday.new do |conn|
  conn.request :retry, max: 2, interval: 0.05,
                       interval_randomness: 0.5, backoff_factor: 2
                       exceptions: [CustomException, 'Timeout::Error']
  conn.adapter ...
end

This example will result in a first interval that is random between 0.05 and 0.075 and a second interval that is random between 0.1 and 0.15

Defined Under Namespace

Classes: Options

Constant Summary collapse

IDEMPOTENT_METHODS =
[:delete, :get, :head, :options, :put]

Instance Method Summary collapse

Methods inherited from Middleware

dependency, inherited, loaded?, new

Methods included from MiddlewareRegistry

#fetch_middleware, #load_middleware, #lookup_middleware, #middleware_mutex, #register_middleware

Constructor Details

#initialize(app, options = nil) ⇒ Retry

Public: Initialize middleware

Options: max - Maximum number of retries (default: 2) interval - Pause in seconds between retries (default: 0) interval_randomness - The maximum random interval amount expressed

as a float between 0 and 1 to use in addition to the
interval. (default: 0)

max_interval - An upper limit for the interval (default: Float::MAX) backoff_factor - The amount to multiple each successive retry’s

interval amount by in order to provide backoff
(default: 1)

exceptions - The list of exceptions to handle. Exceptions can be

given as Class, Module, or String. (default:
[Errno::ETIMEDOUT, Timeout::Error,
Error::TimeoutError])

methods - A list of HTTP methods to retry without calling retry_if. Pass

an empty Array to call retry_if for all exceptions.
(defaults to the idempotent HTTP methods in IDEMPOTENT_METHODS)

retry_if - 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.
(defaults to return false)


97
98
99
100
101
# File 'lib/faraday/request/retry.rb', line 97

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) ⇒ Object

Private: construct an exception matcher object.

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.



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

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
          error.class.to_s == ex.to_s
        end
      end
    end
  end
  matcher
end

#call(env) ⇒ Object



111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
# File 'lib/faraday/request/retry.rb', line 111

def call(env)
  retries = @options.max
  request_body = env[:body]
  begin
    env[:body] = request_body # after failure env[:body] is set to the response body
    @app.call(env)
  rescue @errmatch => exception
    if retries > 0 && retry_request?(env, exception)
      retries -= 1
      sleep sleep_amount(retries + 1)
      retry
    end
    raise
  end
end

#sleep_amount(retries) ⇒ Object



103
104
105
106
107
108
109
# File 'lib/faraday/request/retry.rb', line 103

def sleep_amount(retries)
  retry_index = @options.max - retries
  current_interval = @options.interval * (@options.backoff_factor ** retry_index)
  current_interval = [current_interval, @options.max_interval].min
  random_interval  = rand * @options.interval_randomness.to_f * @options.interval
  current_interval + random_interval
end