exponential.go 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. package backoff
  2. import (
  3. "math/rand"
  4. "time"
  5. )
  6. /*
  7. ExponentialBackOff is an implementation of BackOff that increases
  8. it's back off period for each retry attempt using a randomization function
  9. that grows exponentially.
  10. Backoff() time is calculated using the following formula:
  11. randomized_interval =
  12. retry_interval * (random value in range [1 - randomization_factor, 1 + randomization_factor])
  13. In other words BackOff() will sleep for times between the randomization factor
  14. percentage below and above the retry interval.
  15. For example, using 2 seconds as the base retry interval and 0.5 as the
  16. randomization factor, the actual back off period used in the next retry
  17. attempt will be between 1 and 3 seconds.
  18. Note: max_interval caps the retry_interval and not the randomized_interval.
  19. Example: The default retry_interval is .5 seconds, default randomization_factor
  20. is 0.5, default multiplier is 1.5 and the max_interval is set to 25 seconds.
  21. For 12 tries the sequence will sleep (values in seconds) (output from ExampleExpBackOffTimes) :
  22. request# retry_interval randomized_interval
  23. 1 0.5 [0.25, 0.75]
  24. 2 0.75 [0.375, 1.125]
  25. 3 1.125 [0.562, 1.687]
  26. 4 1.687 [0.8435, 2.53]
  27. 5 2.53 [1.265, 3.795]
  28. 6 3.795 [1.897, 5.692]
  29. 7 5.692 [2.846, 8.538]
  30. 8 8.538 [4.269, 12.807]
  31. 9 12.807 [6.403, 19.210]
  32. 10 19.22 [9.611, 28.833]
  33. 11 25 [12.5, 37.5]
  34. 12 25 [12.5, 37.5]
  35. Implementation is not thread-safe.
  36. */
  37. type ExponentialBackOff struct {
  38. InitialInterval time.Duration
  39. currentInterval time.Duration
  40. MaxInterval time.Duration
  41. RandomizationFactor float64
  42. Multiplier float64
  43. }
  44. // Default values for ExponentialBackOff.
  45. const (
  46. DefaultInitialInterval = 500 * time.Millisecond
  47. DefaultRandomizationFactor = 0.5
  48. DefaultMultiplier = 1.5
  49. DefaultMaxInterval = 60 * time.Second
  50. )
  51. // NewExponential creates an instance of ExponentialBackOff using default values.
  52. func NewExponential() *ExponentialBackOff {
  53. b := &ExponentialBackOff{
  54. InitialInterval: DefaultInitialInterval,
  55. RandomizationFactor: DefaultRandomizationFactor,
  56. Multiplier: DefaultMultiplier,
  57. MaxInterval: DefaultMaxInterval,
  58. currentInterval: DefaultInitialInterval,
  59. }
  60. b.Reset()
  61. return b
  62. }
  63. // Reset the interval back to the initial retry interval and restarts the timer.
  64. func (b *ExponentialBackOff) Reset() {
  65. b.currentInterval = b.InitialInterval
  66. }
  67. func (b *ExponentialBackOff) GetSleepTime() time.Duration {
  68. return getRandomValueFromInterval(b.RandomizationFactor, rand.Float64(), b.currentInterval)
  69. }
  70. func (b *ExponentialBackOff) BackOff() {
  71. time.Sleep(b.GetSleepTime())
  72. b.IncrementCurrentInterval()
  73. }
  74. // Increments the current interval by multiplying it with the multiplier.
  75. func (b *ExponentialBackOff) IncrementCurrentInterval() {
  76. // Check for overflow, if overflow is detected set the current interval to the max interval.
  77. if float64(b.currentInterval) >= float64(b.MaxInterval)/b.Multiplier {
  78. b.currentInterval = b.MaxInterval
  79. } else {
  80. b.currentInterval = time.Duration(float64(b.currentInterval) * b.Multiplier)
  81. }
  82. }
  83. func (b *ExponentialBackOff) Inverval() time.Duration {
  84. return b.currentInterval
  85. }
  86. // Returns a random value from the interval:
  87. // [randomizationFactor * currentInterval, randomizationFactor * currentInterval].
  88. func getRandomValueFromInterval(randomizationFactor, random float64, currentInterval time.Duration) time.Duration {
  89. var delta = randomizationFactor * float64(currentInterval)
  90. var minInterval = float64(currentInterval) - delta
  91. var maxInterval = float64(currentInterval) + delta
  92. // Get a random value from the range [minInterval, maxInterval].
  93. // The formula used below has a +1 because if the minInterval is 1 and the maxInterval is 3 then
  94. // we want a 33% chance for selecting either 1, 2 or 3.
  95. return time.Duration(minInterval + (random * (maxInterval - minInterval + 1)))
  96. }