retry.go 1.4 KB

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