Module: Dinie::Internal::Retry
- Defined in:
- lib/dinie/runtime/retry.rb
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 ‘429, 500, 502, 503, 504`, plus timeouts/connection errors (handled by retryable_network_error?). `409` (Dinie semantic conflict) and `410` (gone) never retry; `401` is a one-shot re-auth handled in HttpClient, orthogonal to this set. `500` is safe to retry on a non-GET because the stable `X-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 = 0` base 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 (‘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 (‘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 |