package util import ( "context" "errors" "fmt" "github.com/openHPI/poseidon/pkg/logging" "time" ) var ( log = logging.GetLogger("util") // MaxConnectionRetriesExponential is the default number of retries. It's exported for testing reasons. MaxConnectionRetriesExponential = 18 // InitialWaitingDuration is the default initial duration of waiting after a failed time. InitialWaitingDuration = time.Second ) func retryExponential(ctx context.Context, sleep time.Duration, f func() error) func() error { return func() error { err := f() if err != nil { select { case <-ctx.Done(): case <-time.After(sleep): sleep *= 2 } } return err } } func retryConstant(ctx context.Context, sleep time.Duration, f func() error) func() error { return func() error { err := f() if err != nil { select { case <-ctx.Done(): return fmt.Errorf("stopped retrying: %w", ctx.Err()) case <-time.After(sleep): } } return err } } func retryAttempts(maxAttempts int, f func() error) (err error) { for i := 0; i < maxAttempts; i++ { err = f() if err == nil { return nil } else if errors.Is(err, context.DeadlineExceeded) || errors.Is(err, context.Canceled) { return err } log.WithField("count", i).WithError(err).Debug("retrying after error") } return err } // RetryExponentialWithContext executes the passed function with exponentially increasing time starting with one second // up to a default maximum number of attempts as long as the context is not done. func RetryExponentialWithContext(ctx context.Context, f func() error) error { return retryAttempts(MaxConnectionRetriesExponential, retryExponential(ctx, InitialWaitingDuration, f)) } // RetryExponential executes the passed function with exponentially increasing time starting with one second // up to a default maximum number of attempts. func RetryExponential(f func() error) error { return retryAttempts(MaxConnectionRetriesExponential, retryExponential(context.Background(), InitialWaitingDuration, f)) } // RetryConstantAttemptsWithContext executes the passed function with a constant retry delay of one second // up to the passed maximum number of attempts as long as the context is not done. func RetryConstantAttemptsWithContext(attempts int, ctx context.Context, f func() error) error { return retryAttempts(attempts, retryConstant(ctx, InitialWaitingDuration, f)) }