File: backoff.go

package info (click to toggle)
gitlab-shell 14.35.0%2Bds1-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 23,652 kB
  • sloc: ruby: 1,129; makefile: 583; sql: 391; sh: 384
file content (75 lines) | stat: -rw-r--r-- 2,941 bytes parent folder | download | duplicates (7)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
// Package backoff implements exponential backoff mechanism based on gRPC's backoff algorithm
// https://github.com/grpc/grpc/blob/master/doc/connection-backoff.md
package backoff

import (
	"math"
	"math/rand"
	"time"
)

// Strategy implements a backoff strategy. This strategy has a single Backoff method that returns
// a time duration corresponding to the input retries.
type Strategy interface {
	Backoff(retries uint) time.Duration
}

// Exponential defines an exponential backoff strategy. It multiplicatively decreases the rate of
// retrial by increasing the wait time. The wait time is calculated to following:
// Backoff(retries) = BaseDelay * (Multiplier ^ retries) + rand(0, Jitter)
// The backoff time can never exceed the MaxDelay.
type Exponential struct {
	// BaseDelay is the minimum delay for the first attempt.
	BaseDelay time.Duration
	// MaxDelay is the upper limit for exponential backoff.
	MaxDelay time.Duration
	// Multiplier is the factor determining "how fast" the delay increases after each retry.
	Multiplier float64
	// Jitter defines the maximum of randomized duration added to the delay of each step. This
	// randomization prevents all actors retry at the same time.
	Jitter time.Duration
	// Random source for randomizing delay of each step
	rand *rand.Rand
}

// NewDefaultExponential returns an exponential backoff strategy using a set of configurations good
// enough for network connection retry.
func NewDefaultExponential(r *rand.Rand) *Exponential {
	//
	// | Retries | Delay before jitter |
	// | ------- | ------------------- |
	// | 0       | 1 second            |
	// | 1       | 1.5 seconds         |
	// | 2       | 2.3 seconds         |
	// | 3       | 3.4 seconds         |
	// | 4       | 5.0 seconds         |
	// | 5       | 7.6 seconds         |
	// | 6       | 11.0 seconds        |
	// | 7       | 17.1 seconds        |
	// | 8       | 25.1 seconds        |
	// | 9       | 38.4 seconds        |
	// | 10      | 57.6 seconds        |
	// | 11      | 60 seconds          |
	// | 12      | 60 seconds          |
	// | ...     | 60 seconds          |
	return &Exponential{
		BaseDelay:  1 * time.Second,
		MaxDelay:   60 * time.Second,
		Multiplier: 1.5,
		Jitter:     200 * time.Millisecond,
		rand:       r,
	}
}

// Backoff returns the duration to wait before retry again. The caller is fully responsible for
// retry controlling retry attempts and timers. Typically, the caller increases the retry attempt
// and creates a timer from the result of this method. Typically, this method is called before
// increasing the attempt of the caller, which is Backoff(0). It means the backoff time after the
// first failure.
func (e *Exponential) Backoff(retries uint) time.Duration {
	backoff := math.Min(
		float64(e.MaxDelay),
		float64(e.BaseDelay)*math.Pow(e.Multiplier, float64(retries)),
	) + float64(e.Jitter)*e.rand.Float64()
	return time.Duration(backoff).Round(time.Millisecond)
}