blob: 15f0c731be8b137a3af8947685217eb55539c5f5 [file]
package utils
import (
"context"
"fmt"
"time"
"github.com/golang/glog"
)
type RetryOptions struct {
// Number of times to try a function before failing.
// If unset, the function will be attempted once.
Attempts int
// The time to wait between function attempts.
Delay time.Duration
// Function which determines whether or not the function should be retried after a given error.
// If this is unset, every error is considered retryable.
ShouldRetry func(error) bool
}
// Retry calls a function repeatedly until it succeeds or another stopping condition is met.
//
// The stopping conditions are determined by the provided RetryOptions.
func Retry(ctx context.Context, fn func(ctx context.Context) error, opts RetryOptions) error {
attempts := opts.Attempts
if attempts <= 0 {
attempts = 1
}
for i := range attempts {
err := fn(ctx)
if err == nil {
return nil
}
if i + 1 == attempts {
break
}
if opts.ShouldRetry == nil || opts.ShouldRetry(err) {
glog.Infof("error %w; retrying", err)
} else {
return fmt.Errorf("fatal error, not retrying: %w", err)
}
if opts.Delay > 0 {
// Block until the delay interval or the context deadline, whichever happens first.
select {
case <-time.After(opts.Delay): {}
case <-ctx.Done():
return fmt.Errorf("context deadline exceeded")
}
}
}
return fmt.Errorf("no successes after %d attempts", opts.Attempts)
}