exponential_backoff.go 1.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960
  1. package frankenphp
  2. import (
  3. "sync"
  4. "time"
  5. )
  6. const maxBackoff = 1 * time.Second
  7. const minBackoff = 100 * time.Millisecond
  8. const maxConsecutiveFailures = 6
  9. type exponentialBackoff struct {
  10. backoff time.Duration
  11. failureCount int
  12. mu sync.RWMutex
  13. upFunc sync.Once
  14. }
  15. func newExponentialBackoff() *exponentialBackoff {
  16. return &exponentialBackoff{backoff: minBackoff}
  17. }
  18. func (e *exponentialBackoff) reset() {
  19. e.mu.Lock()
  20. e.upFunc = sync.Once{}
  21. wait := e.backoff * 2
  22. e.mu.Unlock()
  23. go func() {
  24. time.Sleep(wait)
  25. e.mu.Lock()
  26. defer e.mu.Unlock()
  27. e.upFunc.Do(func() {
  28. // if we come back to a stable state, reset the failure count
  29. if e.backoff == minBackoff {
  30. e.failureCount = 0
  31. }
  32. // earn back the backoff over time
  33. if e.failureCount > 0 {
  34. e.backoff = max(e.backoff/2, minBackoff)
  35. }
  36. })
  37. }()
  38. }
  39. func (e *exponentialBackoff) trigger(onMaxFailures func(failureCount int)) {
  40. e.mu.RLock()
  41. e.upFunc.Do(func() {
  42. if e.failureCount >= maxConsecutiveFailures {
  43. onMaxFailures(e.failureCount)
  44. }
  45. e.failureCount += 1
  46. })
  47. wait := e.backoff
  48. e.mu.RUnlock()
  49. time.Sleep(wait)
  50. e.mu.Lock()
  51. e.backoff = min(e.backoff*2, maxBackoff)
  52. e.mu.Unlock()
  53. }