retry.go 1.5 KB

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