Module: Heatwave::FaradayRetry

Defined in:
app/lib/heatwave/faraday_retry.rb

Overview

Project conventions for Faraday's +:retry+ middleware, replacing the
hand-rolled +Retryable.retryable(on: Retryable::TIMEOUT_CLASSES)+ wrappers
that used to guard outbound HTTP calls one call site at a time.

Apply it when building a Faraday connection:

Faraday.new(url: base) do |f|
f.request :retry, **Heatwave::FaradayRetry.options(tries: 3, base: 4)
f.request :json
f.adapter Faraday.default_adapter
end

The +:retry+ middleware backs off and retries the request inside the
connection, so callers no longer wrap each call in a block. It retries on
the transient transport failures in EXCEPTIONS and, optionally, on a set
of HTTP +retry_statuses+.

== Mapping Retryable semantics onto faraday-retry

The two libraries count and space attempts differently; FaradayRetry.options does the
translation so call sites read the same as the wrappers they replace:

Retryable faraday-retry (this helper)


tries: 3 (3 attempts total) max: 2 (2 retries)
sleep: ->(n){ 4**n } (1s, 4s, 16s, ...) interval: 1, backoff_factor: 4
sleep: 2 (constant 2s) interval: 2, backoff_factor: 1
on: TIMEOUT_CLASSES exceptions: EXCEPTIONS

See Also:

Constant Summary collapse

EXCEPTIONS =

Transient transport failures worth retrying. Mirrors the HTTP-relevant
subset of +Retryable::TIMEOUT_CLASSES+: once the http.rb adapter
(+Faraday.default_adapter == :http+) wraps a request, its own +HTTP::*+
timeout/connection errors surface as Faraday::TimeoutError /
Faraday::ConnectionFailed, so we retry the Faraday-flavored classes
plus the lower-level SSL/socket/DNS errors that can escape the adapter.

Faraday::RetriableResponse is included so the +retry_statuses+ option
(when a call site opts into status-based retries) takes effect.

[
  Faraday::TimeoutError,
  Faraday::ConnectionFailed,
  Faraday::SSLError,
  Faraday::RetriableResponse,
  Timeout::Error,
  Net::ReadTimeout,
  Net::OpenTimeout,
  OpenSSL::SSL::SSLError,
  OpenSSL::SSL::SSLErrorWaitReadable,
  SocketError,          # DNS resolution failures (getaddrinfo)
  Errno::ECONNREFUSED,  # connection refused
  Errno::ECONNRESET,    # peer reset mid-stream
  Errno::EHOSTUNREACH,  # host unreachable
  Errno::ETIMEDOUT      # kernel-level connect timeout
].freeze
ALL_METHODS =

Every HTTP verb the legacy wrappers retried. The +Retryable+ blocks wrapped
the call regardless of method — EDI feed submissions are POSTs and were
retried on timeout — so we opt every verb in rather than accept
faraday-retry's idempotent-only default (IDEMPOTENT_METHODS). Call sites
that should only retry reads pass +methods: %i[get]+ explicitly.

%i[delete get head options patch post put].freeze
IDEMPOTENT_METHODS =

faraday-retry's safe default: the idempotent verbs only. Exposed so a
GET/PUT-only client can opt into the conservative set by name.

%i[delete get head options put].freeze

Class Method Summary collapse

Class Method Details

.options(tries: 3, base: 4, interval: 1, methods: ALL_METHODS, retry_statuses: [], exceptions: EXCEPTIONS) ⇒ Hash

Build the keyword options to splat into +f.request :retry+, translating
the +Retryable+ +tries+/+sleep+ vocabulary into faraday-retry's
+max+/+interval+/+backoff_factor+.

Parameters:

  • tries (Integer) (defaults to: 3)

    total attempts (initial + retries), matching
    +Retryable+'s +:tries+. faraday-retry's +max+ counts retries only, so
    this passes +tries - 1+.

  • base (Numeric) (defaults to: 4)

    exponential backoff base, matching a Retryable
    +sleep+ of +base+ raised to the attempt number (=> +backoff_factor:+).
    Pass +base: 1+ for a constant delay of +interval+ seconds.

  • interval (Numeric) (defaults to: 1)

    delay before the first retry, in seconds.

  • methods (Array<Symbol>) (defaults to: ALL_METHODS)

    HTTP verbs to retry. Defaults to
    ALL_METHODS to preserve the wrappers' retry-any-verb behavior.

  • retry_statuses (Array<Integer>) (defaults to: [])

    HTTP statuses to retry. Empty by
    default so behavior matches the exception-only +Retryable+ wrappers; a
    call site can opt in (e.g. +[429, 503]+) where the upstream documents it.

  • exceptions (Array) (defaults to: EXCEPTIONS)

    exception classes to retry on. Defaults to
    EXCEPTIONS.

Returns:

  • (Hash)

    keyword options for the faraday-retry middleware.



102
103
104
105
106
107
108
109
110
111
# File 'app/lib/heatwave/faraday_retry.rb', line 102

def options(tries: 3, base: 4, interval: 1, methods: ALL_METHODS, retry_statuses: [], exceptions: EXCEPTIONS)
  {
    max: [tries - 1, 0].max,
    interval: interval,
    backoff_factor: base,
    exceptions: exceptions,
    methods: methods,
    retry_statuses: retry_statuses
  }
end