Module: Dinie::Internal::Retry
- Defined in:
- lib/dinie/runtime/retry.rb,
sig/dinie/runtime/retry.rbs
Overview
Retry policy — pure decision + delay functions (no I/O, no state). The retry loop itself
— sleeping, attempt counting, the X-Dinie-Retry-Count header, the 401 one-shot re-auth
— lives in HttpClient (architecture §10, RB15). Mirrors sdk-js retry.ts so the two
SDKs share the exact same backoff/jitter/status-set behavior (the comparison.md axis).
Runtime-internal: imported directly by HttpClient, not part of the public surface. The
public Retry-After parser lives separately as Dinie.parse_retry_after (story 002).
Constant Summary collapse
- RETRYABLE_STATUS =
HTTP status codes the SDK retries (V0.2 freeze decision; architecture §10).
Exactly
{408, 429, 500, 502, 503, 504}, plus timeouts/connection errors (handled by retryable_network_error?).409(Dinie semantic conflict) and410(gone) never retry;401is a one-shot re-auth handled in HttpClient, orthogonal to this set.500is safe to retry on a non-GET because the stableX-Idempotency-Key(minted once before the loop) guarantees a retry never creates a duplicate resource. Set[408, 429, 500, 502, 503, 504].freeze
- INITIAL_BACKOFF_SECONDS =
Initial backoff, in seconds (the
attempt = 0base before jitter). 0.5- MAX_BACKOFF_SECONDS =
Backoff ceiling, in seconds (reached at
attempt = 4). 8- JITTER_RATIO =
Subtractive jitter fraction — the delay is reduced by up to this share.
0.25- RETRYABLE_NETWORK_ERRORS =
Faraday transport errors (no HTTP response) worth retrying: a request timeout and a connection failure (DNS/refused/reset). Anything else propagates as a connection error.
[Faraday::TimeoutError, Faraday::ConnectionFailed].freeze
Class Method Summary collapse
-
.retry_delay(attempt, retry_after: nil, retry_after_ms: nil) ⇒ Float
Seconds to wait before the next attempt.
-
.retryable_network_error?(error) ⇒ Boolean
Whether a thrown transport error (no HTTP response) is worth retrying: a timeout or a connection failure.
-
.should_retry?(status) ⇒ Boolean
True only for the retryable status set (
{408, 429, 500, 502, 503, 504}).
Class Method Details
.retry_delay(attempt, retry_after: nil, retry_after_ms: nil) ⇒ Float
Seconds to wait before the next attempt.
A parseable Retry-After / Retry-After-Ms wins (already clamped to [0, 60] by
Dinie.parse_retry_after). Otherwise: exponential backoff min(0.5 · 2^attempt, 8) s
minus up to 25% subtractive jitter via rand (assert the band in specs, not the value).
65 66 67 68 69 70 71 |
# File 'lib/dinie/runtime/retry.rb', line 65 def retry_delay(attempt, retry_after: nil, retry_after_ms: nil) from_header = Dinie.parse_retry_after(retry_after, retry_after_ms: retry_after_ms) return from_header unless from_header.nil? base = [INITIAL_BACKOFF_SECONDS * (2**attempt), MAX_BACKOFF_SECONDS].min base * (1 - (JITTER_RATIO * rand)) end |
.retryable_network_error?(error) ⇒ Boolean
Whether a thrown transport error (no HTTP response) is worth retrying: a timeout or a connection failure. Keys off the Faraday error class, never the message.
51 52 53 |
# File 'lib/dinie/runtime/retry.rb', line 51 def retryable_network_error?(error) RETRYABLE_NETWORK_ERRORS.any? { |klass| error.is_a?(klass) } end |
.should_retry?(status) ⇒ Boolean
True only for the retryable status set ({408, 429, 500, 502, 503, 504}).
42 43 44 |
# File 'lib/dinie/runtime/retry.rb', line 42 def should_retry?(status) RETRYABLE_STATUS.include?(status) end |