retry.go 1.4 KB

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