backoff.go 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. // Copyright 2023 The frp Authors
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package wait
  15. import (
  16. "math/rand/v2"
  17. "time"
  18. "github.com/fatedier/frp/pkg/util/util"
  19. )
  20. type BackoffFunc func(previousDuration time.Duration, previousConditionError bool) time.Duration
  21. func (f BackoffFunc) Backoff(previousDuration time.Duration, previousConditionError bool) time.Duration {
  22. return f(previousDuration, previousConditionError)
  23. }
  24. type BackoffManager interface {
  25. Backoff(previousDuration time.Duration, previousConditionError bool) time.Duration
  26. }
  27. type FastBackoffOptions struct {
  28. Duration time.Duration
  29. Factor float64
  30. Jitter float64
  31. MaxDuration time.Duration
  32. InitDurationIfFail time.Duration
  33. // If FastRetryCount > 0, then within the FastRetryWindow time window,
  34. // the retry will be performed with a delay of FastRetryDelay for the first FastRetryCount calls.
  35. FastRetryCount int
  36. FastRetryDelay time.Duration
  37. FastRetryJitter float64
  38. FastRetryWindow time.Duration
  39. }
  40. type fastBackoffImpl struct {
  41. options FastBackoffOptions
  42. lastCalledTime time.Time
  43. consecutiveErrCount int
  44. fastRetryCutoffTime time.Time
  45. countsInFastRetryWindow int
  46. }
  47. func NewFastBackoffManager(options FastBackoffOptions) BackoffManager {
  48. return &fastBackoffImpl{
  49. options: options,
  50. countsInFastRetryWindow: 1,
  51. }
  52. }
  53. func (f *fastBackoffImpl) Backoff(previousDuration time.Duration, previousConditionError bool) time.Duration {
  54. if f.lastCalledTime.IsZero() {
  55. f.lastCalledTime = time.Now()
  56. return f.options.Duration
  57. }
  58. now := time.Now()
  59. f.lastCalledTime = now
  60. if previousConditionError {
  61. f.consecutiveErrCount++
  62. } else {
  63. f.consecutiveErrCount = 0
  64. }
  65. if f.options.FastRetryCount > 0 && previousConditionError {
  66. f.countsInFastRetryWindow++
  67. if f.countsInFastRetryWindow <= f.options.FastRetryCount {
  68. return Jitter(f.options.FastRetryDelay, f.options.FastRetryJitter)
  69. }
  70. if now.After(f.fastRetryCutoffTime) {
  71. // reset
  72. f.fastRetryCutoffTime = now.Add(f.options.FastRetryWindow)
  73. f.countsInFastRetryWindow = 0
  74. }
  75. }
  76. if previousConditionError {
  77. var duration time.Duration
  78. if f.consecutiveErrCount == 1 {
  79. duration = util.EmptyOr(f.options.InitDurationIfFail, previousDuration)
  80. } else {
  81. duration = previousDuration
  82. }
  83. duration = util.EmptyOr(duration, time.Second)
  84. if f.options.Factor != 0 {
  85. duration = time.Duration(float64(duration) * f.options.Factor)
  86. }
  87. if f.options.Jitter > 0 {
  88. duration = Jitter(duration, f.options.Jitter)
  89. }
  90. if f.options.MaxDuration > 0 && duration > f.options.MaxDuration {
  91. duration = f.options.MaxDuration
  92. }
  93. return duration
  94. }
  95. return f.options.Duration
  96. }
  97. func BackoffUntil(f func() (bool, error), backoff BackoffManager, sliding bool, stopCh <-chan struct{}) {
  98. var delay time.Duration
  99. previousError := false
  100. ticker := time.NewTicker(backoff.Backoff(delay, previousError))
  101. defer ticker.Stop()
  102. for {
  103. select {
  104. case <-stopCh:
  105. return
  106. default:
  107. }
  108. if !sliding {
  109. delay = backoff.Backoff(delay, previousError)
  110. }
  111. if done, err := f(); done {
  112. return
  113. } else if err != nil {
  114. previousError = true
  115. } else {
  116. previousError = false
  117. }
  118. if sliding {
  119. delay = backoff.Backoff(delay, previousError)
  120. }
  121. ticker.Reset(delay)
  122. select {
  123. case <-stopCh:
  124. return
  125. case <-ticker.C:
  126. }
  127. }
  128. }
  129. // Jitter returns a time.Duration between duration and duration + maxFactor *
  130. // duration.
  131. //
  132. // This allows clients to avoid converging on periodic behavior. If maxFactor
  133. // is 0.0, a suggested default value will be chosen.
  134. func Jitter(duration time.Duration, maxFactor float64) time.Duration {
  135. if maxFactor <= 0.0 {
  136. maxFactor = 1.0
  137. }
  138. wait := duration + time.Duration(rand.Float64()*maxFactor*float64(duration))
  139. return wait
  140. }
  141. func Until(f func(), period time.Duration, stopCh <-chan struct{}) {
  142. ff := func() (bool, error) {
  143. f()
  144. return false, nil
  145. }
  146. BackoffUntil(ff, BackoffFunc(func(time.Duration, bool) time.Duration {
  147. return period
  148. }), true, stopCh)
  149. }