OSDN Git Service

Merge pull request #201 from Bytom/v0.1
[bytom/vapor.git] / vendor / github.com / cenkalti / backoff / exponential.go
diff --git a/vendor/github.com/cenkalti/backoff/exponential.go b/vendor/github.com/cenkalti/backoff/exponential.go
new file mode 100644 (file)
index 0000000..a031a65
--- /dev/null
@@ -0,0 +1,153 @@
+package backoff
+
+import (
+       "math/rand"
+       "time"
+)
+
+/*
+ExponentialBackOff is a backoff implementation that increases the backoff
+period for each retry attempt using a randomization function that grows exponentially.
+
+NextBackOff() is calculated using the following formula:
+
+ randomized interval =
+     RetryInterval * (random value in range [1 - RandomizationFactor, 1 + RandomizationFactor])
+
+In other words NextBackOff() will range between the randomization factor
+percentage below and above the retry interval.
+
+For example, given the following parameters:
+
+ RetryInterval = 2
+ RandomizationFactor = 0.5
+ Multiplier = 2
+
+the actual backoff period used in the next retry attempt will range between 1 and 3 seconds,
+multiplied by the exponential, that is, between 2 and 6 seconds.
+
+Note: MaxInterval caps the RetryInterval and not the randomized interval.
+
+If the time elapsed since an ExponentialBackOff instance is created goes past the
+MaxElapsedTime, then the method NextBackOff() starts returning backoff.Stop.
+
+The elapsed time can be reset by calling Reset().
+
+Example: Given the following default arguments, for 10 tries the sequence will be,
+and assuming we go over the MaxElapsedTime on the 10th try:
+
+ Request #  RetryInterval (seconds)  Randomized Interval (seconds)
+
+  1          0.5                     [0.25,   0.75]
+  2          0.75                    [0.375,  1.125]
+  3          1.125                   [0.562,  1.687]
+  4          1.687                   [0.8435, 2.53]
+  5          2.53                    [1.265,  3.795]
+  6          3.795                   [1.897,  5.692]
+  7          5.692                   [2.846,  8.538]
+  8          8.538                   [4.269, 12.807]
+  9         12.807                   [6.403, 19.210]
+ 10         19.210                   backoff.Stop
+
+Note: Implementation is not thread-safe.
+*/
+type ExponentialBackOff struct {
+       InitialInterval     time.Duration
+       RandomizationFactor float64
+       Multiplier          float64
+       MaxInterval         time.Duration
+       // After MaxElapsedTime the ExponentialBackOff stops.
+       // It never stops if MaxElapsedTime == 0.
+       MaxElapsedTime time.Duration
+       Clock          Clock
+
+       currentInterval time.Duration
+       startTime       time.Time
+}
+
+// Clock is an interface that returns current time for BackOff.
+type Clock interface {
+       Now() time.Time
+}
+
+// Default values for ExponentialBackOff.
+const (
+       DefaultInitialInterval     = 500 * time.Millisecond
+       DefaultRandomizationFactor = 0.5
+       DefaultMultiplier          = 1.5
+       DefaultMaxInterval         = 60 * time.Second
+       DefaultMaxElapsedTime      = 15 * time.Minute
+)
+
+// NewExponentialBackOff creates an instance of ExponentialBackOff using default values.
+func NewExponentialBackOff() *ExponentialBackOff {
+       b := &ExponentialBackOff{
+               InitialInterval:     DefaultInitialInterval,
+               RandomizationFactor: DefaultRandomizationFactor,
+               Multiplier:          DefaultMultiplier,
+               MaxInterval:         DefaultMaxInterval,
+               MaxElapsedTime:      DefaultMaxElapsedTime,
+               Clock:               SystemClock,
+       }
+       b.Reset()
+       return b
+}
+
+type systemClock struct{}
+
+func (t systemClock) Now() time.Time {
+       return time.Now()
+}
+
+// SystemClock implements Clock interface that uses time.Now().
+var SystemClock = systemClock{}
+
+// Reset the interval back to the initial retry interval and restarts the timer.
+func (b *ExponentialBackOff) Reset() {
+       b.currentInterval = b.InitialInterval
+       b.startTime = b.Clock.Now()
+}
+
+// NextBackOff calculates the next backoff interval using the formula:
+//     Randomized interval = RetryInterval +/- (RandomizationFactor * RetryInterval)
+func (b *ExponentialBackOff) NextBackOff() time.Duration {
+       // Make sure we have not gone over the maximum elapsed time.
+       if b.MaxElapsedTime != 0 && b.GetElapsedTime() > b.MaxElapsedTime {
+               return Stop
+       }
+       defer b.incrementCurrentInterval()
+       return getRandomValueFromInterval(b.RandomizationFactor, rand.Float64(), b.currentInterval)
+}
+
+// GetElapsedTime returns the elapsed time since an ExponentialBackOff instance
+// is created and is reset when Reset() is called.
+//
+// The elapsed time is computed using time.Now().UnixNano(). It is
+// safe to call even while the backoff policy is used by a running
+// ticker.
+func (b *ExponentialBackOff) GetElapsedTime() time.Duration {
+       return b.Clock.Now().Sub(b.startTime)
+}
+
+// Increments the current interval by multiplying it with the multiplier.
+func (b *ExponentialBackOff) incrementCurrentInterval() {
+       // Check for overflow, if overflow is detected set the current interval to the max interval.
+       if float64(b.currentInterval) >= float64(b.MaxInterval)/b.Multiplier {
+               b.currentInterval = b.MaxInterval
+       } else {
+               b.currentInterval = time.Duration(float64(b.currentInterval) * b.Multiplier)
+       }
+}
+
+// Returns a random value from the following interval:
+//     [randomizationFactor * currentInterval, randomizationFactor * currentInterval].
+func getRandomValueFromInterval(randomizationFactor, random float64, currentInterval time.Duration) time.Duration {
+       var delta = randomizationFactor * float64(currentInterval)
+       var minInterval = float64(currentInterval) - delta
+       var maxInterval = float64(currentInterval) + delta
+
+       // Get a random value from the range [minInterval, maxInterval].
+       // The formula used below has a +1 because if the minInterval is 1 and the maxInterval is 3 then
+       // we want a 33% chance for selecting either 1, 2 or 3.
+       return time.Duration(minInterval + (random * (maxInterval - minInterval + 1)))
+}