github.com/laof/lite-speed-test@v0.0.0-20230930011949-1f39b7037845/utils/retry.go (about) 1 package utils 2 3 import ( 4 "errors" 5 "time" 6 ) 7 8 var ( 9 ErrRetryFailed = errors.New("all retry attempts failed") 10 ) 11 12 // Strategy is a way to retry on a specific function. 13 type Strategy interface { 14 // On performs a retry on a specific function, until it doesn't return any error. 15 On(func() error) error 16 } 17 18 type retryer struct { 19 totalAttempt int 20 nextDelay func() uint32 21 } 22 23 // On implements Strategy.On. 24 func (r *retryer) On(method func() error) error { 25 attempt := 0 26 accumulatedError := make([]error, 0, r.totalAttempt) 27 for attempt < r.totalAttempt { 28 err := method() 29 if err == nil { 30 return nil 31 } 32 numErrors := len(accumulatedError) 33 if numErrors == 0 || err.Error() != accumulatedError[numErrors-1].Error() { 34 accumulatedError = append(accumulatedError, err) 35 } 36 delay := r.nextDelay() 37 time.Sleep(time.Duration(delay) * time.Millisecond) 38 attempt++ 39 } 40 return ErrRetryFailed 41 } 42 43 // Timed returns a retry strategy with fixed interval. 44 func Timed(attempts int, delay uint32) Strategy { 45 return &retryer{ 46 totalAttempt: attempts, 47 nextDelay: func() uint32 { 48 return delay 49 }, 50 } 51 } 52 53 func ExponentialBackoff(attempts int, delay uint32) Strategy { 54 nextDelay := uint32(0) 55 return &retryer{ 56 totalAttempt: attempts, 57 nextDelay: func() uint32 { 58 r := nextDelay 59 nextDelay += delay 60 return r 61 }, 62 } 63 }