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  }