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