cloudflare/cloudflared

Public

mirrored from https://github.com/cloudflare/cloudflaredAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
2018.11.0

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

origin/backoffhandler.go

95lines · modecode

1package origin
2
3import (
4 "time"
5
6 "golang.org/x/net/context"
7)
8
9// Redeclare time functions so they can be overridden in tests.
10var (
11 timeNow = time.Now
12 timeAfter = time.After
13)
14
15// BackoffHandler manages exponential backoff and limits the maximum number of retries.
16// The base time period is 1 second, doubling with each retry.
17// After initial success, a grace period can be set to reset the backoff timer if
18// a connection is maintained successfully for a long enough period. The base grace period
19// is 2 seconds, doubling with each retry.
20type BackoffHandler struct {
21 // MaxRetries sets the maximum number of retries to perform. The default value
22 // of 0 disables retry completely.
23 MaxRetries uint
24 // RetryForever caps the exponential backoff period according to MaxRetries
25 // but allows you to retry indefinitely.
26 RetryForever bool
27 // BaseTime sets the initial backoff period.
28 BaseTime time.Duration
29
30 retries uint
31 resetDeadline time.Time
32}
33
34func (b BackoffHandler) GetBackoffDuration(ctx context.Context) (time.Duration, bool) {
35 // Follows the same logic as Backoff, but without mutating the receiver.
36 // This select has to happen first to reflect the actual behaviour of the Backoff function.
37 select {
38 case <-ctx.Done():
39 return time.Duration(0), false
40 default:
41 }
42 if !b.resetDeadline.IsZero() && timeNow().After(b.resetDeadline) {
43 // b.retries would be set to 0 at this point
44 return time.Second, true
45 }
46 if b.retries >= b.MaxRetries && !b.RetryForever {
47 return time.Duration(0), false
48 }
49 return time.Duration(b.GetBaseTime() * 1 << b.retries), true
50}
51
52// BackoffTimer returns a channel that sends the current time when the exponential backoff timeout expires.
53// Returns nil if the maximum number of retries have been used.
54func (b *BackoffHandler) BackoffTimer() <-chan time.Time {
55 if !b.resetDeadline.IsZero() && timeNow().After(b.resetDeadline) {
56 b.retries = 0
57 b.resetDeadline = time.Time{}
58 }
59 if b.retries >= b.MaxRetries {
60 if !b.RetryForever {
61 return nil
62 }
63 } else {
64 b.retries++
65 }
66 return timeAfter(time.Duration(b.GetBaseTime() * 1 << (b.retries - 1)))
67}
68
69// Backoff is used to wait according to exponential backoff. Returns false if the
70// maximum number of retries have been used or if the underlying context has been cancelled.
71func (b *BackoffHandler) Backoff(ctx context.Context) bool {
72 c := b.BackoffTimer()
73 if c == nil {
74 return false
75 }
76 select {
77 case <-c:
78 return true
79 case <-ctx.Done():
80 return false
81 }
82}
83
84// Sets a grace period within which the the backoff timer is maintained. After the grace
85// period expires, the number of retries & backoff duration is reset.
86func (b *BackoffHandler) SetGracePeriod() {
87 b.resetDeadline = timeNow().Add(time.Duration(b.GetBaseTime() * 2 << b.retries))
88}
89
90func (b BackoffHandler) GetBaseTime() time.Duration {
91 if b.BaseTime == 0 {
92 return time.Second
93 }
94 return b.BaseTime
95}
96