Files
httpx/retry/backoff.go
Aleksey Shakhmatov 505c7b8c4f Add retry transport with configurable backoff and Retry-After support
Implements retry middleware as a RoundTripper wrapper:
- Exponential and constant backoff strategies with jitter
- RFC 7231 Retry-After header parsing (seconds and HTTP-date)
- Default policy retries idempotent methods on 429/5xx and network errors
- Body restoration via GetBody, context cancellation, response body cleanup
2026-03-20 14:21:53 +03:00

65 lines
1.3 KiB
Go

package retry
import (
"math/rand/v2"
"time"
)
// Backoff computes the delay before the next retry attempt.
type Backoff interface {
// Delay returns the wait duration for the given attempt number (zero-based).
Delay(attempt int) time.Duration
}
// ExponentialBackoff returns a Backoff that doubles the delay on each attempt.
// The delay is calculated as base * 2^attempt, capped at max. When withJitter
// is true, a random duration in [0, delay*0.5) is added.
func ExponentialBackoff(base, max time.Duration, withJitter bool) Backoff {
return &exponentialBackoff{
base: base,
max: max,
withJitter: withJitter,
}
}
// ConstantBackoff returns a Backoff that always returns the same delay.
func ConstantBackoff(d time.Duration) Backoff {
return constantBackoff{delay: d}
}
type exponentialBackoff struct {
base time.Duration
max time.Duration
withJitter bool
}
func (b *exponentialBackoff) Delay(attempt int) time.Duration {
delay := b.base
for range attempt {
delay *= 2
if delay >= b.max {
delay = b.max
break
}
}
if b.withJitter {
jitter := time.Duration(rand.Int64N(int64(delay / 2)))
delay += jitter
}
if delay > b.max {
delay = b.max
}
return delay
}
type constantBackoff struct {
delay time.Duration
}
func (b constantBackoff) Delay(_ int) time.Duration {
return b.delay
}