backoff.go 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. // Copyright 2012-present Oliver Eilhard. All rights reserved.
  2. // Use of this source code is governed by a MIT-license.
  3. // See http://olivere.mit-license.org/license.txt for details.
  4. package elastic
  5. import (
  6. "math"
  7. "math/rand"
  8. "sync"
  9. "time"
  10. )
  11. // BackoffFunc specifies the signature of a function that returns the
  12. // time to wait before the next call to a resource. To stop retrying
  13. // return false in the 2nd return value.
  14. type BackoffFunc func(retry int) (time.Duration, bool)
  15. // Backoff allows callers to implement their own Backoff strategy.
  16. type Backoff interface {
  17. // Next implements a BackoffFunc.
  18. Next(retry int) (time.Duration, bool)
  19. }
  20. // -- ZeroBackoff --
  21. // ZeroBackoff is a fixed backoff policy whose backoff time is always zero,
  22. // meaning that the operation is retried immediately without waiting,
  23. // indefinitely.
  24. type ZeroBackoff struct{}
  25. // Next implements BackoffFunc for ZeroBackoff.
  26. func (b ZeroBackoff) Next(retry int) (time.Duration, bool) {
  27. return 0, true
  28. }
  29. // -- StopBackoff --
  30. // StopBackoff is a fixed backoff policy that always returns false for
  31. // Next(), meaning that the operation should never be retried.
  32. type StopBackoff struct{}
  33. // Next implements BackoffFunc for StopBackoff.
  34. func (b StopBackoff) Next(retry int) (time.Duration, bool) {
  35. return 0, false
  36. }
  37. // -- ConstantBackoff --
  38. // ConstantBackoff is a backoff policy that always returns the same delay.
  39. type ConstantBackoff struct {
  40. interval time.Duration
  41. }
  42. // NewConstantBackoff returns a new ConstantBackoff.
  43. func NewConstantBackoff(interval time.Duration) *ConstantBackoff {
  44. return &ConstantBackoff{interval: interval}
  45. }
  46. // Next implements BackoffFunc for ConstantBackoff.
  47. func (b *ConstantBackoff) Next(retry int) (time.Duration, bool) {
  48. return b.interval, true
  49. }
  50. // -- Exponential --
  51. // ExponentialBackoff implements the simple exponential backoff described by
  52. // Douglas Thain at http://dthain.blogspot.de/2009/02/exponential-backoff-in-distributed.html.
  53. type ExponentialBackoff struct {
  54. t float64 // initial timeout (in msec)
  55. f float64 // exponential factor (e.g. 2)
  56. m float64 // maximum timeout (in msec)
  57. }
  58. // NewExponentialBackoff returns a ExponentialBackoff backoff policy.
  59. // Use initialTimeout to set the first/minimal interval
  60. // and maxTimeout to set the maximum wait interval.
  61. func NewExponentialBackoff(initialTimeout, maxTimeout time.Duration) *ExponentialBackoff {
  62. return &ExponentialBackoff{
  63. t: float64(int64(initialTimeout / time.Millisecond)),
  64. f: 2.0,
  65. m: float64(int64(maxTimeout / time.Millisecond)),
  66. }
  67. }
  68. // Next implements BackoffFunc for ExponentialBackoff.
  69. func (b *ExponentialBackoff) Next(retry int) (time.Duration, bool) {
  70. r := 1.0 + rand.Float64() // random number in [1..2]
  71. m := math.Min(r*b.t*math.Pow(b.f, float64(retry)), b.m)
  72. if m >= b.m {
  73. return 0, false
  74. }
  75. d := time.Duration(int64(m)) * time.Millisecond
  76. return d, true
  77. }
  78. // -- Simple Backoff --
  79. // SimpleBackoff takes a list of fixed values for backoff intervals.
  80. // Each call to Next returns the next value from that fixed list.
  81. // After each value is returned, subsequent calls to Next will only return
  82. // the last element. The values are optionally "jittered" (off by default).
  83. type SimpleBackoff struct {
  84. sync.Mutex
  85. ticks []int
  86. jitter bool
  87. }
  88. // NewSimpleBackoff creates a SimpleBackoff algorithm with the specified
  89. // list of fixed intervals in milliseconds.
  90. func NewSimpleBackoff(ticks ...int) *SimpleBackoff {
  91. return &SimpleBackoff{
  92. ticks: ticks,
  93. jitter: false,
  94. }
  95. }
  96. // Jitter enables or disables jittering values.
  97. func (b *SimpleBackoff) Jitter(flag bool) *SimpleBackoff {
  98. b.Lock()
  99. b.jitter = flag
  100. b.Unlock()
  101. return b
  102. }
  103. // jitter randomizes the interval to return a value of [0.5*millis .. 1.5*millis].
  104. func jitter(millis int) int {
  105. if millis <= 0 {
  106. return 0
  107. }
  108. return millis/2 + rand.Intn(millis)
  109. }
  110. // Next implements BackoffFunc for SimpleBackoff.
  111. func (b *SimpleBackoff) Next(retry int) (time.Duration, bool) {
  112. b.Lock()
  113. defer b.Unlock()
  114. if retry >= len(b.ticks) {
  115. return 0, false
  116. }
  117. ms := b.ticks[retry]
  118. if b.jitter {
  119. ms = jitter(ms)
  120. }
  121. return time.Duration(ms) * time.Millisecond, true
  122. }