gitlab.com/beacon-software/gadget@v0.0.0-20181217202115-54565ea1ed5e/net/backoff.go (about) 1 package net 2 3 import ( 4 "math" 5 "math/rand" 6 "time" 7 ) 8 9 const ( 10 // DefaultMaxRetries is used as the maximum retries in the backoff when Backoff is called. 11 DefaultMaxRetries = 5 12 // DefaultMinimumCycle is used as the minimum cycle duration when Backoff is called. 13 DefaultMinimumCycle = 100 * time.Millisecond 14 // DefaultCeiling is used as the maximum wait time between executions when Backoff is called. 15 DefaultCeiling = 15 * time.Second 16 ) 17 18 // F is a function that will be called sequentially 19 type F func() error 20 21 // Backoff Executes the passed function with exponential back off up to a maximum a number of tries with the 22 // minimum amount of time per cycle using default values. 23 func Backoff(f F) error { 24 return BackoffExtended(f, DefaultMaxRetries, DefaultMinimumCycle, DefaultCeiling) 25 } 26 27 // BackoffExtended Executes the passed function with exponential back off up to a maximum a number of tries with the 28 // minimum amount of time per cycle. 29 func BackoffExtended(f F, maxTries int, minimumCycle time.Duration, maxCycle time.Duration) error { 30 var err error 31 r := rand.New(rand.NewSource(time.Now().UnixNano())) 32 for n := 1; n < maxTries+1; n++ { 33 err = f() 34 if nil == err { 35 break 36 } 37 wait := CalculateBackoff(r, n, minimumCycle, maxCycle) 38 time.Sleep(wait) 39 } 40 return err 41 } 42 43 // CalculateBackoff returns a duration for exponential backoff 44 func CalculateBackoff(r *rand.Rand, attempt int, minimumCycle time.Duration, maxCycle time.Duration) time.Duration { 45 minMS := uint(minimumCycle.Seconds() * 1000) 46 // if min cycle is 1 millisecond or smaller bring it over 1 to make the exponentiation work 47 if minMS <= 1 { 48 minMS = 2 49 } 50 maxMS := maxCycle.Seconds() * 1000 51 full := math.Min(math.Pow(float64(minMS), float64(attempt)), maxMS) 52 // exponentiation will be 90% of our calculated value with maxCycle as a ceiling 53 exp := full * 0.9 54 // r.Float64() is in [0.0, 1.0] 55 jitter := r.Float64() * float64(full*0.1) 56 // exponentiation plus jitter 57 return time.Duration(exp+jitter) * time.Millisecond 58 }