1
1
// Copyright (c) HashiCorp, Inc.
2
2
// SPDX-License-Identifier: MPL-2.0
3
3
4
- package retry2
4
+ package backoff
5
5
6
6
import (
7
7
"context"
@@ -20,7 +20,7 @@ type Timer interface {
20
20
// DelayFunc returns the duration to wait before the next retry attempt.
21
21
type DelayFunc func (uint ) time.Duration
22
22
23
- // FixedDelay returns a delay that is the same through all iterations except the first (when it is 0) .
23
+ // FixedDelay returns a delay. The first retry attempt has no delay (0), and subsequent attempts use the fixed delay .
24
24
func FixedDelay (delay time.Duration ) DelayFunc {
25
25
return func (n uint ) time.Duration {
26
26
if n == 0 {
@@ -35,21 +35,24 @@ func FixedDelay(delay time.Duration) DelayFunc {
35
35
// to pick the same deterministic random sequence.
36
36
var rng = rand .New (rand .NewSource (time .Now ().UnixNano ()))
37
37
38
- // ExponentialBackoffWithJitterDelay returns a duration of backoffMinDuration * backoffMultiplier**n, with added jitter.
39
- func ExponentialBackoffWithJitterDelay (backoffMinDuration time.Duration , backoffMultiplier float64 ) DelayFunc {
38
+ // ExponentialJitterBackoff returns a duration of backoffMinDuration * backoffMultiplier**n, with added jitter.
39
+ func ExponentialJitterBackoff (backoffMinDuration time.Duration , backoffMultiplier float64 ) DelayFunc {
40
40
return func (n uint ) time.Duration {
41
41
if n == 0 {
42
42
return 0
43
43
}
44
44
45
45
mult := math .Pow (backoffMultiplier , float64 (n ))
46
- d := time .Duration (float64 (backoffMinDuration ) * mult )
47
- const jitter = 0.4
48
- mult = 1 - jitter * rng .Float64 () // Subtract up to 40%.
49
- return time .Duration (float64 (d ) * mult )
46
+ return applyJitter (time .Duration (float64 (backoffMinDuration ) * mult ))
50
47
}
51
48
}
52
49
50
+ func applyJitter (base time.Duration ) time.Duration {
51
+ const jitterFactor = 0.4
52
+ jitter := 1 - jitterFactor * rng .Float64 () // Subtract up to 40%.
53
+ return time .Duration (float64 (base ) * jitter )
54
+ }
55
+
53
56
type sdkv2HelperRetryCompatibleDelay struct {
54
57
minTimeout time.Duration
55
58
pollInterval time.Duration
@@ -105,20 +108,20 @@ func DefaultSDKv2HelperRetryCompatibleDelay() DelayFunc {
105
108
return SDKv2HelperRetryCompatibleDelay (0 , 0 , 500 * time .Millisecond ) //nolint:mnd // 500ms is the Plugin SDKv2 default
106
109
}
107
110
108
- // Config configures a retry loop.
109
- type Config struct {
111
+ // RetryConfig configures a retry loop.
112
+ type RetryConfig struct {
110
113
delay DelayFunc
111
114
gracePeriod time.Duration
112
115
timer Timer
113
116
}
114
117
115
118
// Option represents an option for retry.
116
- type Option func (* Config )
119
+ type Option func (* RetryConfig )
117
120
118
- func emptyOption (c * Config ) {}
121
+ func emptyOption (c * RetryConfig ) {}
119
122
120
123
func WithGracePeriod (d time.Duration ) Option {
121
- return func (c * Config ) {
124
+ return func (c * RetryConfig ) {
122
125
c .gracePeriod = d
123
126
}
124
127
}
@@ -128,7 +131,7 @@ func WithDelay(d DelayFunc) Option {
128
131
return emptyOption
129
132
}
130
133
131
- return func (c * Config ) {
134
+ return func (c * RetryConfig ) {
132
135
c .delay = d
133
136
}
134
137
}
@@ -137,7 +140,7 @@ func WithDelay(d DelayFunc) Option {
137
140
// This primarily is useful for mocking/testing, where you may not want to explicitly wait for a set duration
138
141
// for retries.
139
142
func WithTimer (t Timer ) Option {
140
- return func (c * Config ) {
143
+ return func (c * RetryConfig ) {
141
144
c .timer = t
142
145
}
143
146
}
@@ -149,45 +152,45 @@ func (t *timerImpl) After(d time.Duration) <-chan time.Time {
149
152
return time .After (d )
150
153
}
151
154
152
- // The default Config is backwards compatible with github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry.
153
- func defaultConfig () Config {
154
- return Config {
155
+ // The default RetryConfig is backwards compatible with github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry.
156
+ func defaultRetryConfig () RetryConfig {
157
+ return RetryConfig {
155
158
delay : DefaultSDKv2HelperRetryCompatibleDelay (),
156
159
gracePeriod : 30 * time .Second ,
157
160
timer : & timerImpl {},
158
161
}
159
162
}
160
163
161
- // RetryWithTimeout holds state for managing retry loops with a timeout.
162
- type RetryWithTimeout struct {
164
+ // RetryLoop holds state for managing retry loops with a timeout.
165
+ type RetryLoop struct {
163
166
attempt uint
164
- config Config
167
+ config RetryConfig
165
168
deadline deadline
166
169
timedOut bool
167
170
}
168
171
169
- // BeginWithOptions returns a new retry loop configured with the provided options.
170
- func BeginWithOptions (timeout time.Duration , opts ... Option ) * RetryWithTimeout {
171
- config := defaultConfig ()
172
+ // NewRetryLoopWithOptions returns a new retry loop configured with the provided options.
173
+ func NewRetryLoopWithOptions (timeout time.Duration , opts ... Option ) * RetryLoop {
174
+ config := defaultRetryConfig ()
172
175
for _ , opt := range opts {
173
176
opt (& config )
174
177
}
175
178
176
- return & RetryWithTimeout {
179
+ return & RetryLoop {
177
180
config : config ,
178
181
deadline : NewDeadline (timeout + config .gracePeriod ),
179
182
}
180
183
}
181
184
182
- // Begin returns a new retry loop configured with the default options.
183
- func Begin (timeout time.Duration ) * RetryWithTimeout {
184
- return BeginWithOptions (timeout )
185
+ // NewRetryLoop returns a new retry loop configured with the default options.
186
+ func NewRetryLoop (timeout time.Duration ) * RetryLoop {
187
+ return NewRetryLoopWithOptions (timeout )
185
188
}
186
189
187
190
// Continue sleeps between retry attempts.
188
191
// It returns false if the timeout has been exceeded.
189
192
// The deadline is not checked on the first call to Continue.
190
- func (r * RetryWithTimeout ) Continue (ctx context.Context ) bool {
193
+ func (r * RetryLoop ) Continue (ctx context.Context ) bool {
191
194
if r .attempt != 0 && r .deadline .Remaining () == 0 {
192
195
r .timedOut = true
193
196
@@ -200,18 +203,18 @@ func (r *RetryWithTimeout) Continue(ctx context.Context) bool {
200
203
return true
201
204
}
202
205
203
- // Reset resets a RetryWithTimeout to its initial state.
204
- func (r * RetryWithTimeout ) Reset () {
206
+ // Reset resets a RetryLoop to its initial state.
207
+ func (r * RetryLoop ) Reset () {
205
208
r .attempt = 0
206
209
}
207
210
208
211
// TimedOut return whether the retry timed out.
209
- func (r * RetryWithTimeout ) TimedOut () bool {
212
+ func (r * RetryLoop ) TimedOut () bool {
210
213
return r .timedOut
211
214
}
212
215
213
- // Sleeps for the specified duration or until context is done , whichever occurs first.
214
- func (r * RetryWithTimeout ) sleep (ctx context.Context , d time.Duration ) {
216
+ // sleep sleeps for the specified duration or until the context is canceled , whichever occurs first.
217
+ func (r * RetryLoop ) sleep (ctx context.Context , d time.Duration ) {
215
218
if d == 0 {
216
219
return
217
220
}
0 commit comments