RetryFunc with capped exponential backoff and jitter
Go
package retry
import (
"context"
"fmt"
"math/rand"
"time"
)
// RetryFunc calls fn until it succeeds, attempts run out, or ctx is done.
// Delays grow exponentially from base, capped at maxDelay, with full jitter.
func RetryFunc(ctx context.Context, attempts int, base, maxDelay time.Duration, fn func(context.Context) error) error {
attempts = max(attempts, 1)
var last error
for i := 0; i < attempts; i++ {
if err := ctx.Err(); err != nil {
return err
}
if last = fn(ctx); last == nil {
return nil
}
if i == attempts-1 {
break
}
delay := min(base<<i, maxDelay)
if delay > 0 {
wait := time.Duration(rand.Int63n(int64(delay)))
select {
case <-ctx.Done():
return ctx.Err()
case <-time.After(wait):
}
}
}
return fmt.Errorf("retry: %d attempts failed: %w", attempts, last)
}