summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/segmentio/backo-go/backo.go
blob: 6f7b6d5e25d5a31bbcfb8800d1b73d71320eda1e (plain)
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
76
77
78
79
80
81
82
83
package backo

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

type Backo struct {
	base   time.Duration
	factor uint8
	jitter float64
	cap    time.Duration
}

// Creates a backo instance with the given parameters
func NewBacko(base time.Duration, factor uint8, jitter float64, cap time.Duration) *Backo {
	return &Backo{base, factor, jitter, cap}
}

// Creates a backo instance with the following defaults:
//   base: 100 milliseconds
//   factor: 2
//   jitter: 0
//   cap: 10 seconds
func DefaultBacko() *Backo {
	return NewBacko(time.Millisecond*100, 2, 0, time.Second*10)
}

// Duration returns the backoff interval for the given attempt.
func (backo *Backo) Duration(attempt int) time.Duration {
	duration := float64(backo.base) * math.Pow(float64(backo.factor), float64(attempt))

	if backo.jitter != 0 {
		random := rand.Float64()
		deviation := math.Floor(random * backo.jitter * duration)
		if (int(math.Floor(random*10)) & 1) == 0 {
			duration = duration - deviation
		} else {
			duration = duration + deviation
		}
	}

	duration = math.Min(float64(duration), float64(backo.cap))
	return time.Duration(duration)
}

// Sleep pauses the current goroutine for the backoff interval for the given attempt.
func (backo *Backo) Sleep(attempt int) {
	duration := backo.Duration(attempt)
	time.Sleep(duration)
}

type Ticker struct {
	done chan struct{}
	C    <-chan time.Time
}

func (b *Backo) NewTicker() *Ticker {
	c := make(chan time.Time, 1)
	ticker := &Ticker{
		done: make(chan struct{}, 1),
		C:    c,
	}

	go func() {
		for i := 0; ; i++ {
			select {
			case t := <-time.After(b.Duration(i)):
				c <- t
			case <-ticker.done:
				close(c)
				return
			}
		}
	}()

	return ticker
}

func (t *Ticker) Stop() {
	t.done <- struct{}{}
}