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