nurse/internal/retry/retry.go

48 lines
1.1 KiB
Go
Raw Normal View History

package retry
import (
"context"
"errors"
"time"
)
// Retry executes a function with the given number of attempts and attempt timeouts.
// It returns the last error encountered during the attempts.
// If the context is canceled, it returns the context error (if there is no previous error),
// or the joined error of the last error and the context error (otherwise).
func Retry(
ctx context.Context,
numberOfAttempts uint,
attemptTimeout time.Duration,
f func(ctx context.Context, attempt int) error,
) (lastErr error) {
baseCtx, baseCancel := context.WithTimeout(ctx, time.Duration(numberOfAttempts)*attemptTimeout)
defer baseCancel()
for i := uint(0); i < numberOfAttempts; i++ {
select {
case <-ctx.Done():
if lastErr == nil {
return ctx.Err()
}
return errors.Join(lastErr, ctx.Err())
default:
attemptCtx, attemptCancel := context.WithTimeout(baseCtx, attemptTimeout)
lastErr = f(attemptCtx, int(i))
if lastErr == nil {
attemptCancel()
return nil
}
if attemptCtx.Err() == nil {
<-attemptCtx.Done()
}
attemptCancel()
}
}
return lastErr
}