cloudflare/cloudflared

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
2019.8.4

Branches

Tags

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

Clone

HTTPS

Download ZIP

origin/backoffhandler.go

94lines · modecode

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