Table of contents
Part of nurago, a collection of independent Go packages for backend services.
import "github.com/tecnickcom/nurago/pkg/retrier"
Package retrier provides a configurable retry engine for executing a task function with backoff, jitter, and per-attempt timeouts.
The delay schedule and jitter strategies are those of backoff, so one retry policy can be applied consistently to HTTP and non-HTTP work.
How It Works
New creates a Retrier with defaults, or with custom Option values.
Retrier.Run then executes a TaskFn according to configured retry rules:
- Execute the task with a per-attempt timeout context.
- Evaluate the result with a retry predicate (
RetryIfFn). - Stop when attempts are exhausted or retry is not required.
- Otherwise schedule the next attempt after delay + random jitter.
- Increase delay by the configured multiplication factor for successive retries.
The run loop always respects parent context cancellation.
Defaults
- attempts:
DefaultAttempts(4) - initial delay:
DefaultDelay(1s) - delay factor:
DefaultDelayFactor(2) - jitter:
DefaultJitter(1ms) - per-attempt timeout:
DefaultTimeout(1s) - maximum delay: unbounded (use
WithMaxDelayto cap the pre-jitter backoff) - retry condition:
DefaultRetryIf(retry on any non-nil error)
Usage
r, err := retrier.New(
retrier.WithAttempts(5),
retrier.WithDelay(200*time.Millisecond),
retrier.WithDelayFactor(2),
retrier.WithJitter(25*time.Millisecond),
)
if err != nil {
return err
}
err = r.Run(ctx, func(ctx context.Context) error {
return callExternalService(ctx)
})
if err != nil {
return err
}
When To Use
- A task talks to a flaky dependency and should be retried on a subset of errors.
- Each attempt needs its own timeout, separate from the overall deadline.
- You want retry decisions driven by a predicate on the returned error.
Example
var count int
// example function that returns nil only at the third attempt.
task := func(_ context.Context) error {
if count == 2 {
return nil
}
count++
return errors.New("ERROR")
}
opts := []retrier.Option{
retrier.WithRetryIfFn(retrier.DefaultRetryIf),
retrier.WithAttempts(5),
retrier.WithDelay(10 * time.Millisecond),
retrier.WithDelayFactor(1.1),
retrier.WithJitter(5 * time.Millisecond),
retrier.WithTimeout(2 * time.Millisecond),
}
r, err := retrier.New(opts...)
if err != nil {
log.Fatal(err)
}
timeout := 1 * time.Second
ctx, cancel := context.WithTimeout(context.TODO(), timeout)
err = r.Run(ctx, task)
cancel()
if err != nil {
log.Fatal(err)
}
fmt.Println(count)
// Output:
// 2
Full source is in example_retrier_test.go. More runnable examples are on pkg.go.dev.
Dependencies
This package reaches no external module: it uses only the Go standard library.