◐ Off-By-One · answer catalog

err-retry-exponential-backoff

1 answer(s)gogo1.26

err-retry-exponential-backoff

📦 Source in repository (JSON)

Answer

The implementation is in backoff/backoff.go — a fully generic (T any) exponential backoff retry library in Go 1.26.

Core API

// Retry a function with exponential backoff.
result, err := backoff.Retry(ctx, config, fn)

// Mark an error as non-retryable (short-circuits the loop).
return "", backoff.Permanent(errors.New("fatal error"))

Usage example

ctx := context.Background()
cfg := backoff.DefaultConfig()
cfg.MaxRetries = 3

users, err := backoff.Retry(ctx, cfg, func(ctx context.Context) ([]User, error) {
    return db.QueryUsers(ctx)
})

Architecture

Feature Implementation
Max retries Config field, initial attempt not counted
Initial delay Base delay before first retry
Max delay Caps exponential growth via if delay > maxDelay { delay = maxDelay }
Multiplier delay = time.Duration(float64(delay) * config.Multiplier)
Jitter rand.Int63n(int64(delay) + 1) — uniform [0, delay)
Retryable predicate IsRetryable func(error) bool — returns immediately if false
Permanent errors backoff.Permanent(err) — errors.As unwrapping, loop bail
Context cancellation Checked before sleep and inside sleepWithContext via select on ctx.Done()
Callback OnRetry(attempt, delay, err) invoked before each retry

All config parameters are validated in Config.Validate() before the retry loop begins.


Evidence & signatures

All 20 tests pass (including 6 sub-tests):

```text
=== RUN   TestRetry_FirstAttemptSuccess       --- PASS
=== RUN   TestRetry_EventuallySucceeds        --- PASS  (3 attempts → success)
=== RUN   TestRetry_ExhaustsRetries           --- PASS  (returns last error)
=== RUN   TestRetry_NonRetryableError         --- PASS  (1 attempt, predicate stops it)
=== RUN   TestRetry_ContextCancelledDuringBackoff --- PASS (cancellation during sleep)
=== RUN   TestRetry_ContextCancelledDuringCall --- PASS (cancellation inside fn)
=== RUN   TestRetry_DeadlineExceeded          --- PASS  (context deadline)
=== RUN   TestRetry_PermanentError            --- PASS  (1 attempt, Permanent short-circuits)
=== RUN   TestPermanent_Nil                   --- PASS
=== RUN   TestRetry_OnRetryCallback           --- PASS  (callback invoked per attempt)
=== RUN   TestRetry_JitterVariability         --- PASS  (non-trivial elapsed time)
=== RUN   TestRetry_MaxDelayCap               --- PASS  (delay capped at 25ms)
=== RUN   TestRetry_ZeroMaxRetries            --- PASS  (no retries, immediate fail)
=== RUN   TestRetry_RetryablePredicateSelective --- PASS (retry vs stop on different errors)
=== RUN   TestConfig_Validation               --- PASS  (6 sub-cases)
=== RUN   TestRetry_ManyRetries               --- PASS  (20 retries, no issues)
=== RUN   TestRetry_ContextValue              --- PASS
=== RUN   TestPermanentError_Unwrap           --- PASS
=== RUN   TestRetry_ZeroDelay                 --- PASS
=== RUN   ExampleRetry                        --- PASS
PASS
```

**Edge cases tested:**
1. **Success on first attempt** — no unnecessary delay
2. **Exhausts all retries** — returns the last error from the loop
3. **Non-retryable predicate** — returns immediately without retrying
4. **Context cancellation during backoff sleep** — respects cancellation within ~10ms
5. **Context cancellation from inside the function** — passes `ctx.Err()` through
6. **Deadline exceeded** — returns `context.DeadlineExceeded`
7. **Permanent error** — `backoff.Permanent()` short-circuits in one attempt
8. **Zero max retries** — only the initial attempt runs
9. **Zero initial delay** — no sleep, works correctly
10. **Max delay cap** — exponential growth bounded to 25ms
11. **Jitter** — randomized delays produce varying total time
12. **Large retry count** — 20 retries with jitter and max delay handle gracefully
13. **Nil Permanent** — returns nil safely

Benchmark: `BenchmarkRetry_FastFailure-16` — **37,743 ns/op** (2 calls with 1μs delay).

---
{"model": "gpt-4o", "problem_class": "err-retry-exponential-backoff", "result": "passed", "tests": 20}
Generated from the verified corpus · MIT licensedBack to the catalog