pkg/utils/retry/retry.go (45 lines of code) (raw):
// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
// or more contributor license agreements. Licensed under the Elastic License 2.0;
// you may not use this file except in compliance with the Elastic License 2.0.
package retry
import (
"fmt"
"time"
)
// ErrTimeoutReached is an error returned when timeout is reached
type ErrTimeoutReached struct {
Timeout time.Duration
}
func (e *ErrTimeoutReached) Error() string {
return fmt.Sprintf("timeout reached after %s", e.Timeout)
}
// UntilSuccess retries the given function f for up to the given timeout,
// by separating each attempt by the given retryInterval.
//
// f is considered successful if it does not return an error.
// In case the timeout is reached before the first failure of f,
// an ErrTimeoutReached is returned.
// Otherwise, the error from the last attempt is returned.
func UntilSuccess(f func() error, timeout time.Duration, retryInterval time.Duration) error {
totalTimer := time.NewTimer(timeout)
defer totalTimer.Stop()
var lastErr error
errorToReturn := func() error {
if lastErr == nil {
return &ErrTimeoutReached{Timeout: timeout}
}
return lastErr
}
for {
resp := make(chan (error))
go func() {
resp <- f()
}()
select {
case <-totalTimer.C:
return errorToReturn()
case err := <-resp:
if err == nil {
return nil
}
lastErr = err
retryTimer := time.NewTimer(retryInterval)
select {
case <-retryTimer.C:
retryTimer.Stop()
continue
case <-totalTimer.C:
return errorToReturn()
}
}
}
}