mirror of
https://github.com/portainer/portainer.git
synced 2026-06-23 06:50:12 +00:00
81 lines
1.8 KiB
Go
81 lines
1.8 KiB
Go
package retry
|
|
|
|
import (
|
|
"math/rand/v2"
|
|
"time"
|
|
|
|
"github.com/rs/zerolog/log"
|
|
)
|
|
|
|
type Settings struct {
|
|
MaxRetries int
|
|
RetryTimeFunc func(failures int) time.Duration
|
|
}
|
|
|
|
var Default = Settings{
|
|
MaxRetries: 3,
|
|
RetryTimeFunc: RetryTime,
|
|
}
|
|
|
|
// RetryWithWarnings executes a function with retries and backoff, logging warnings on failures
|
|
func RetryWithWarnings[T any](operation string, settings Settings, fn func() (T, error)) (T, error) {
|
|
timeStart := time.Now()
|
|
|
|
var result T
|
|
var err error
|
|
|
|
for attempt := 1; attempt <= settings.MaxRetries; attempt++ {
|
|
result, err = fn()
|
|
if err == nil {
|
|
if attempt > 1 {
|
|
log.Info().
|
|
Str("operation", operation).
|
|
Int("attempt", attempt).
|
|
Str("duration", time.Since(timeStart).String()).
|
|
Msg("operation succeeded after retry")
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
if attempt < settings.MaxRetries {
|
|
retryDelay := settings.RetryTimeFunc(attempt)
|
|
log.Warn().
|
|
Err(err).
|
|
Str("operation", operation).
|
|
Int("attempt", attempt).
|
|
Int("max_retries", settings.MaxRetries).
|
|
Str("retry_delay", retryDelay.String()). // the string version of the duration reads better in logs
|
|
Str("duration", time.Since(timeStart).String()).
|
|
Msg("operation failed, retrying")
|
|
time.Sleep(retryDelay)
|
|
}
|
|
}
|
|
|
|
log.Error().
|
|
Err(err).
|
|
Str("operation", operation).
|
|
Int("attempts", settings.MaxRetries).
|
|
Str("duration", time.Since(timeStart).String()).
|
|
Msg("operation failed after all retries")
|
|
|
|
return result, err
|
|
}
|
|
|
|
func RetryTime(failures int) time.Duration {
|
|
var baseTime time.Duration
|
|
|
|
switch failures {
|
|
case 0, 1:
|
|
baseTime = 5 * time.Second
|
|
case 2:
|
|
baseTime = 30 * time.Second
|
|
default:
|
|
baseTime = 90 * time.Second
|
|
}
|
|
|
|
// plus random jitter
|
|
jitter := time.Duration(rand.Int64N(int64(10 * time.Second))) // 10s as nanoseconds overflows int on 32bit systems
|
|
|
|
return baseTime + jitter
|
|
}
|