github.com/searKing/golang/go@v1.2.74/time/backoff.go (about) 1 // Copyright 2020 The searKing Author. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package time 6 7 import ( 8 "math" 9 "time" 10 ) 11 12 // see https://cloud.google.com/iot/docs/how-tos/exponential-backoff 13 // Implementation of {@link BackOff} that increases the back off period for each retry attempt using 14 // a randomization function that grows exponentially. 15 // 16 // <p>{@link #NextBackOff()} is calculated using the following formula: 17 // 18 // <pre> 19 // randomized_interval = 20 // retry_interval // (random value in range [1 - randomization_factor, 1 + randomization_factor]) 21 // </pre> 22 // 23 // <p>In other words {@link #NextBackOff()} will range between the randomization factor 24 // percentage below and above the retry interval. For example, using 2 seconds as the base retry 25 // interval and 0.5 as the randomization factor, the actual back off period used in the next retry 26 // attempt will be between 1 and 3 seconds. 27 // 28 // <p><b>Note:</b> max_interval caps the retry_interval and not the randomized_interval. 29 // 30 // <p>If the time elapsed since an {@link ExponentialBackOff} instance is created goes past the 31 // max_elapsed_time then the method {@link #NextBackOff()} starts returning {@link 32 // BackOff#STOP}. The elapsed time can be reset by calling {@link #reset()}. 33 // 34 // <p>Example: The default retry_interval is .5 seconds, default randomization_factor is 0.5, 35 // default multiplier is 1.5 and the default max_interval is 1 minute. For 10 tries the sequence 36 // will be (values in seconds) and assuming we go over the max_elapsed_time on the 10th try: 37 // 38 // <pre> 39 // request# retry_interval randomized_interval 40 // 41 // 1 0.5 [0.25, 0.75] 42 // 2 0.75 [0.375, 1.125] 43 // 3 1.125 [0.562, 1.687] 44 // 4 1.687 [0.8435, 2.53] 45 // 5 2.53 [1.265, 3.795] 46 // 6 3.795 [1.897, 5.692] 47 // 7 5.692 [2.846, 8.538] 48 // 8 8.538 [4.269, 12.807] 49 // 9 12.807 [6.403, 19.210] 50 // 10 19.210 {@link BackOff#STOP} 51 // </pre> 52 // 53 // <p>Implementation is not thread-safe. 54 // 55 56 const ( 57 58 // DefaultInitialInterval The default initial interval value (0.5 seconds). 59 DefaultInitialInterval = 500 * time.Millisecond 60 61 // DefaultRandomizationFactor The default randomization factor (0.5 which results in a random period ranging between 50% 62 // below and 50% above the retry interval). 63 DefaultRandomizationFactor = 0.5 64 65 // DefaultMultiplier The default multiplier value (1.5 which is 50% increase per back off). 66 DefaultMultiplier = 1.5 67 68 // DefaultMaxInterval The default maximum back off time (1 minute). 69 DefaultMaxInterval = time.Minute 70 71 // DefaultMaxElapsedDuration The default maximum elapsed time (15 minutes). 72 DefaultMaxElapsedDuration = 15 * time.Minute 73 74 // DefaultMaxElapsedCount The default maximum elapsed count (-1). 75 DefaultMaxElapsedCount = -1 76 ) 77 78 // BackOff 79 // Code borrowed from https://github.com/googleapis/google-http-java-client/blob/master/google-http-client/ 80 // src/main/java/com/google/api/client/util/BackOff.java 81 type BackOff interface { 82 // Reset to initial state. 83 Reset() 84 85 // NextBackOff Gets duration to wait before retrying the operation to 86 // indicate that no retries should be made. 87 // ok indicates that no more retries should be made, max duration is returned also. 88 // Example usage: 89 // var backOffDuration, ok = backoff.NextBackOff(); 90 // if (!ok) { 91 // // do not retry operation 92 // } else { 93 // // sleep for backOffDuration milliseconds and retry operation 94 // } 95 NextBackOff() (backoff time.Duration, ok bool) 96 } 97 98 // ZeroBackOff Fixed back-off policy whose back-off time is always zero, meaning that the operation is retried 99 // immediately without waiting. 100 const ZeroBackOff = NonSlidingBackOff(0) 101 102 // StopBackOff Fixed back-off policy that always returns {@code #STOP} for {@link #NextBackOff()}, 103 // meaning that the operation should not be retried. 104 type StopBackOff struct{} 105 106 func (o *StopBackOff) Reset() {} 107 func (o *StopBackOff) NextBackOff() (backoff time.Duration, ok bool) { 108 return 0, false 109 } 110 111 // NonSlidingBackOff Fixed back-off policy whose back-off time is always const, meaning that the operation is retried 112 // after waiting every duration. 113 type NonSlidingBackOff time.Duration 114 115 func (o *NonSlidingBackOff) Reset() {} 116 func (o *NonSlidingBackOff) NextBackOff() (backoff time.Duration, ok bool) { 117 return time.Duration(*o), false 118 } 119 120 // JitterBackOff returns a time.Duration between 121 // [duration - maxFactor*duration, duration + maxFactor*duration]. 122 // 123 // This allows clients to avoid converging on periodic behavior. 124 func JitterBackOff(duration time.Duration, maxFactor float64) *jitterBackOff { 125 return &jitterBackOff{ 126 duration: duration, 127 maxFactor: maxFactor, 128 } 129 } 130 131 type jitterBackOff struct { 132 duration time.Duration 133 maxFactor float64 134 } 135 136 func (o *jitterBackOff) Reset() {} 137 func (o *jitterBackOff) NextBackOff() (backoff time.Duration, ok bool) { 138 return Jitter(o.duration, o.maxFactor), false 139 } 140 141 // ExponentialBackOff Code borrowed from https://github.com/googleapis/google-http-java-client/blob/master/google-http-client/ 142 // src/main/java/com/google/api/client/util/ExponentialBackOff.java 143 //go:generate go-option -type "ExponentialBackOff" 144 type ExponentialBackOff struct { 145 // The current retry interval. 146 currentInterval time.Duration 147 // The initial retry interval. 148 initialInterval time.Duration 149 // The current retry count. 150 currentCount int 151 152 // The randomization factor to use for creating a range around the retry interval. 153 // A randomization factor of 0.5 results in a random period ranging between 50% below and 50% 154 // above the retry interval. 155 randomizationFactor float64 156 157 // The value to multiply the current interval with for each retry attempt. 158 multiplier float64 159 160 // The maximum value of the back off period. Once the retry interval reaches this 161 // value it stops increasing. 162 // It takes no effect If maxInterval < 0 163 maxInterval time.Duration 164 165 // The system time in nanoseconds. It is calculated when an ExponentialBackOffPolicy instance is 166 // created and is reset when {@link #reset()} is called. 167 startTime time.Time 168 169 // The maximum elapsed time after instantiating {@link ExponentialBackOff} or calling {@link 170 // #reset()} after which {@link #NextBackOff()} returns {@link BackOff#STOP}. 171 // It takes no effect If maxElapsedDuration < 0 172 maxElapsedDuration time.Duration 173 174 // The maximum elapsed count after instantiating {@link ExponentialBackOff} or calling {@link 175 // #reset()} after which {@link #NextBackOff()} returns {@link BackOff#STOP}. 176 // It takes no effect If maxElapsedCount < 0 177 maxElapsedCount int 178 } 179 180 func (o *ExponentialBackOff) SetDefault() { 181 o.initialInterval = DefaultInitialInterval 182 o.randomizationFactor = DefaultRandomizationFactor 183 o.multiplier = DefaultMultiplier 184 o.maxInterval = DefaultMaxInterval 185 o.maxElapsedDuration = DefaultMaxElapsedDuration 186 o.maxElapsedCount = DefaultMaxElapsedCount 187 } 188 189 // NewExponentialBackOff returns a no limit backoff 190 func NewExponentialBackOff(opts ...ExponentialBackOffOption) *ExponentialBackOff { 191 opts = append([]ExponentialBackOffOption{WithExponentialBackOffOptionNoLimit()}, opts...) 192 o := &ExponentialBackOff{} 193 o.SetDefault() 194 o.ApplyOptions(opts...) 195 o.Reset() 196 return o 197 } 198 199 // NewDefaultExponentialBackOff returns a backoff with default limit 200 func NewDefaultExponentialBackOff(opts ...ExponentialBackOffOption) *ExponentialBackOff { 201 o := &ExponentialBackOff{} 202 o.SetDefault() 203 o.ApplyOptions(opts...) 204 o.Reset() 205 return o 206 } 207 208 // NewGrpcExponentialBackOff is a backoff from configuration with the default values specified 209 // at https://github.com/grpc/grpc/blob/master/doc/connection-backoff.md. 210 // 211 // This should be useful for callers who want to configure backoff with 212 // non-default values only for a subset of the options. 213 func NewGrpcExponentialBackOff(opts ...ExponentialBackOffOption) *ExponentialBackOff { 214 opts = append([]ExponentialBackOffOption{WithExponentialBackOffOptionGRPC()}, opts...) 215 o := &ExponentialBackOff{} 216 o.SetDefault() 217 o.ApplyOptions(opts...) 218 o.Reset() 219 return o 220 } 221 222 // Reset Sets the interval back to the initial retry interval and restarts the timer. 223 func (o *ExponentialBackOff) Reset() { 224 o.currentInterval = o.initialInterval 225 o.currentCount = 0 226 o.startTime = time.Now() 227 } 228 229 // NextBackOff This method calculates the next back off interval using the formula: randomized_interval = 230 // retry_interval +/- (randomization_factor * retry_interval) 231 // Subclasses may override if a different algorithm is required. 232 func (o *ExponentialBackOff) NextBackOff() (backoff time.Duration, ok bool) { 233 // Make sure we have not gone over the maximum elapsed count. 234 if o.maxElapsedCount > 0 && o.GetElapsedCount() >= o.maxElapsedCount { 235 return o.currentInterval, false 236 } 237 238 // Make sure we have not gone over the maximum elapsed time. 239 if o.maxElapsedDuration > 0 && o.GetElapsedDuration() > o.maxElapsedDuration { 240 return o.currentInterval, false 241 } 242 243 randomizedInterval := o.GetRandomValueFromInterval(o.randomizationFactor, o.currentInterval) 244 o.incrementCurrentInterval() 245 o.incrementCurrentCount() 246 return randomizedInterval, true 247 } 248 249 // GetRandomValueFromInterval Returns a random value from the interval 250 // [randomizationFactor * currentInterval, randomizationFactor * currentInterval]. 251 func (o *ExponentialBackOff) GetRandomValueFromInterval( 252 randomizationFactor float64, currentInterval time.Duration) time.Duration { 253 return Jitter(currentInterval, randomizationFactor) 254 } 255 256 // GetInitialInterval Returns the initial retry interval. 257 func (o *ExponentialBackOff) GetInitialInterval() time.Duration { 258 return o.initialInterval 259 } 260 261 // GetRandomizationFactor Returns the randomization factor to use for creating a range around the retry interval. 262 // A randomization factor of 0.5 results in a random period ranging between 50% below and 50% 263 // above the retry interval. 264 func (o *ExponentialBackOff) GetRandomizationFactor() float64 { 265 return o.randomizationFactor 266 } 267 268 // GetCurrentInterval Returns the current retry interval. 269 func (o *ExponentialBackOff) GetCurrentInterval() time.Duration { 270 return o.currentInterval 271 } 272 273 // GetMultiplier Returns the value to multiply the current interval with for each retry attempt. 274 func (o *ExponentialBackOff) GetMultiplier() float64 { 275 return o.multiplier 276 } 277 278 // GetMaxInterval Returns the maximum value of the back off period. Once the current interval 279 // reaches this value it stops increasing. 280 func (o *ExponentialBackOff) GetMaxInterval() time.Duration { 281 return o.maxInterval 282 } 283 284 // GetMaxElapsedDuration Returns the maximum elapsed time. 285 // If the time elapsed since an {@link ExponentialBackOff} instance is created goes past the 286 // max_elapsed_time then the method {@link #NextBackOff()} starts returning STOP. 287 // The elapsed time can be reset by calling 288 func (o *ExponentialBackOff) GetMaxElapsedDuration() time.Duration { 289 return o.maxElapsedDuration 290 } 291 292 // GetElapsedDuration Returns the elapsed time since an {@link ExponentialBackOff} instance is 293 // created and is reset when {@link #reset()} is called. 294 // The elapsed time is computed using {@link System#nanoTime()}. 295 func (o *ExponentialBackOff) GetElapsedDuration() time.Duration { 296 return time.Now().Sub(o.startTime) 297 } 298 299 // GetElapsedCount Returns the elapsed count since an {@link ExponentialBackOff} instance is 300 // created and is reset when {@link #reset()} is called. 301 func (o *ExponentialBackOff) GetElapsedCount() int { 302 return o.currentCount 303 } 304 305 // Increments the current interval by multiplying it with the multiplier. 306 func (o *ExponentialBackOff) incrementCurrentInterval() { 307 // Check for overflow, if overflow is detected set the current interval to the max interval. 308 if o.maxInterval >= 0 && o.currentInterval*time.Duration(o.multiplier) >= o.maxInterval { 309 o.currentInterval = o.maxInterval 310 return 311 } 312 o.currentInterval = time.Duration(float64(o.currentInterval) * o.multiplier) 313 } 314 315 // Increments the current count by ++. 316 func (o *ExponentialBackOff) incrementCurrentCount() { 317 // Check for overflow, if overflow is detected set the current interval to the max interval. 318 if o.currentCount >= math.MaxInt64 { 319 o.currentCount = math.MaxInt64 320 return 321 } 322 o.currentCount++ 323 }