|
|
@ -2,6 +2,7 @@ package retry
|
|
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"context"
|
|
|
|
|
|
|
|
"fmt"
|
|
|
|
"math/rand"
|
|
|
|
"math/rand"
|
|
|
|
"time"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
)
|
|
|
@ -30,7 +31,7 @@ func NewJitter(percent int64) Jitter {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Waiter records the number of failures and performs exponential backoff when
|
|
|
|
// Waiter records the number of failures and performs exponential backoff when
|
|
|
|
// when there are consecutive failures.
|
|
|
|
// there are consecutive failures.
|
|
|
|
type Waiter struct {
|
|
|
|
type Waiter struct {
|
|
|
|
// MinFailures before exponential backoff starts. Any failures before
|
|
|
|
// MinFailures before exponential backoff starts. Any failures before
|
|
|
|
// MinFailures is reached will wait MinWait time.
|
|
|
|
// MinFailures is reached will wait MinWait time.
|
|
|
@ -117,3 +118,23 @@ func (w *Waiter) Wait(ctx context.Context) error {
|
|
|
|
func (w *Waiter) NextWait() time.Duration {
|
|
|
|
func (w *Waiter) NextWait() time.Duration {
|
|
|
|
return w.delay()
|
|
|
|
return w.delay()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// RetryLoop retries an operation until either operation completes without error
|
|
|
|
|
|
|
|
// or Waiter's context is canceled.
|
|
|
|
|
|
|
|
func (w *Waiter) RetryLoop(ctx context.Context, operation func() error) error {
|
|
|
|
|
|
|
|
var lastError error
|
|
|
|
|
|
|
|
for {
|
|
|
|
|
|
|
|
if err := w.Wait(ctx); err != nil {
|
|
|
|
|
|
|
|
// The error will only be non-nil if the context is canceled.
|
|
|
|
|
|
|
|
return fmt.Errorf("could not retry operation: %w", lastError)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if err := operation(); err == nil {
|
|
|
|
|
|
|
|
// Reset the failure count seen by the waiter if there was no error.
|
|
|
|
|
|
|
|
w.Reset()
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
lastError = err
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|