github.com/weaveworks/common@v0.0.0-20230728070032-dd9e68f319d5/backoff/backoff.go (about) 1 package backoff 2 3 import ( 4 "time" 5 6 log "github.com/sirupsen/logrus" 7 ) 8 9 type backoff struct { 10 f func() (bool, error) 11 quit, done chan struct{} 12 msg string 13 initialBackoff, maxBackoff time.Duration 14 } 15 16 // Interface does f in a loop, sleeping for initialBackoff between 17 // each iterations. If it hits an error, it exponentially backs 18 // off to maxBackoff. Backoff will log when it backs off, but 19 // will stop logging when it reaches maxBackoff. It will also 20 // log on first success in the beginning and after errors. 21 type Interface interface { 22 Start() 23 Stop() 24 SetInitialBackoff(time.Duration) 25 SetMaxBackoff(time.Duration) 26 } 27 28 // New makes a new Interface 29 func New(f func() (bool, error), msg string) Interface { 30 return &backoff{ 31 f: f, 32 quit: make(chan struct{}), 33 done: make(chan struct{}), 34 msg: msg, 35 initialBackoff: 10 * time.Second, 36 maxBackoff: 60 * time.Second, 37 } 38 } 39 40 func (b *backoff) SetInitialBackoff(d time.Duration) { 41 b.initialBackoff = d 42 } 43 44 func (b *backoff) SetMaxBackoff(d time.Duration) { 45 b.maxBackoff = d 46 } 47 48 // Stop the backoff, and waits for it to stop. 49 func (b *backoff) Stop() { 50 close(b.quit) 51 <-b.done 52 } 53 54 // Start the backoff. Can only be called once. 55 func (b *backoff) Start() { 56 defer close(b.done) 57 backoff := b.initialBackoff 58 shouldLog := true 59 60 for { 61 done, err := b.f() 62 if done { 63 return 64 } 65 66 if err != nil { 67 backoff *= 2 68 shouldLog = true 69 if backoff > b.maxBackoff { 70 backoff = b.maxBackoff 71 shouldLog = false 72 } 73 } else { 74 backoff = b.initialBackoff 75 } 76 77 if shouldLog { 78 if err != nil { 79 log.Warnf("Error %s, backing off %s: %s", 80 b.msg, backoff, err) 81 } else { 82 log.Infof("Success %s", b.msg) 83 } 84 } 85 86 // Re-enable logging if we came from an error (suppressed or not) 87 // since we want to log in case a success follows. 88 shouldLog = err != nil 89 90 select { 91 case <-time.After(backoff): 92 case <-b.quit: 93 return 94 } 95 } 96 97 }