backoff.go 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. // modify from https://github.com/jpillora/backoff/blob/v1.0.0/backoff.go
  2. package slowdown
  3. import (
  4. "math"
  5. "math/rand"
  6. "sync/atomic"
  7. "time"
  8. )
  9. // Backoff is a time.Duration counter, starting at Min. After every call to
  10. // the Duration method the current timing is multiplied by Factor, but it
  11. // never exceeds Max.
  12. //
  13. // Backoff is not generally concurrent-safe, but the ForAttempt method can
  14. // be used concurrently.
  15. type Backoff struct {
  16. attempt atomic.Uint64
  17. // Factor is the multiplying factor for each increment step
  18. Factor float64
  19. // Jitter eases contention by randomizing backoff steps
  20. Jitter bool
  21. // Min and Max are the minimum and maximum values of the counter
  22. Min, Max time.Duration
  23. }
  24. // Duration returns the duration for the current attempt before incrementing
  25. // the attempt counter. See ForAttempt.
  26. func (b *Backoff) Duration() time.Duration {
  27. d := b.ForAttempt(float64(b.attempt.Add(1) - 1))
  28. return d
  29. }
  30. const maxInt64 = float64(math.MaxInt64 - 512)
  31. // ForAttempt returns the duration for a specific attempt. This is useful if
  32. // you have a large number of independent Backoffs, but don't want use
  33. // unnecessary memory storing the Backoff parameters per Backoff. The first
  34. // attempt should be 0.
  35. //
  36. // ForAttempt is concurrent-safe.
  37. func (b *Backoff) ForAttempt(attempt float64) time.Duration {
  38. // Zero-values are nonsensical, so we use
  39. // them to apply defaults
  40. min := b.Min
  41. if min <= 0 {
  42. min = 100 * time.Millisecond
  43. }
  44. max := b.Max
  45. if max <= 0 {
  46. max = 10 * time.Second
  47. }
  48. if min >= max {
  49. // short-circuit
  50. return max
  51. }
  52. factor := b.Factor
  53. if factor <= 0 {
  54. factor = 2
  55. }
  56. //calculate this duration
  57. minf := float64(min)
  58. durf := minf * math.Pow(factor, attempt)
  59. if b.Jitter {
  60. durf = rand.Float64()*(durf-minf) + minf
  61. }
  62. //ensure float64 wont overflow int64
  63. if durf > maxInt64 {
  64. return max
  65. }
  66. dur := time.Duration(durf)
  67. //keep within bounds
  68. if dur < min {
  69. return min
  70. }
  71. if dur > max {
  72. return max
  73. }
  74. return dur
  75. }
  76. // Reset restarts the current attempt counter at zero.
  77. func (b *Backoff) Reset() {
  78. b.attempt.Store(0)
  79. }
  80. // Attempt returns the current attempt counter value.
  81. func (b *Backoff) Attempt() float64 {
  82. return float64(b.attempt.Load())
  83. }
  84. // Copy returns a backoff with equals constraints as the original
  85. func (b *Backoff) Copy() *Backoff {
  86. return &Backoff{
  87. Factor: b.Factor,
  88. Jitter: b.Jitter,
  89. Min: b.Min,
  90. Max: b.Max,
  91. }
  92. }