github.com/searKing/golang/go@v1.2.117/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 // 100 // immediately without waiting. 101 const ZeroBackOff = NonSlidingBackOff(0) 102 103 // StopBackOff Fixed back-off policy that always returns {@code #STOP} for {@link #NextBackOff()}, 104 // meaning that the operation should not be retried. 105 type StopBackOff struct{} 106 107 func (o *StopBackOff) Reset() {} 108 func (o *StopBackOff) NextBackOff() (backoff time.Duration, ok bool) { 109 return 0, false 110 } 111 112 // NonSlidingBackOff Fixed back-off policy whose back-off time is always const, meaning that the operation is retried 113 // after waiting every duration. 114 type NonSlidingBackOff time.Duration 115 116 func (o *NonSlidingBackOff) Reset() {} 117 func (o *NonSlidingBackOff) NextBackOff() (backoff time.Duration, ok bool) { 118 return time.Duration(*o), false 119 } 120 121 // JitterBackOff returns a time.Duration between 122 // [duration - maxFactor*duration, duration + maxFactor*duration]. 123 // 124 // This allows clients to avoid converging on periodic behavior. 125 func JitterBackOff(duration time.Duration, maxFactor float64) *jitterBackOff { 126 return &jitterBackOff{ 127 duration: duration, 128 maxFactor: maxFactor, 129 } 130 } 131 132 type jitterBackOff struct { 133 duration time.Duration 134 maxFactor float64 135 } 136 137 func (o *jitterBackOff) Reset() {} 138 func (o *jitterBackOff) NextBackOff() (backoff time.Duration, ok bool) { 139 return Jitter(o.duration, o.maxFactor), false 140 } 141 142 // ExponentialBackOff Code borrowed from https://github.com/googleapis/google-http-java-client/blob/master/google-http-client/ 143 // src/main/java/com/google/api/client/util/ExponentialBackOff.java 144 // 145 //go:generate go-option -type "ExponentialBackOff" 146 type ExponentialBackOff struct { 147 // The current retry interval. 148 currentInterval time.Duration 149 // The initial retry interval. 150 initialInterval time.Duration 151 // The current retry count. 152 currentCount int 153 154 // The randomization factor to use for creating a range around the retry interval. 155 // A randomization factor of 0.5 results in a random period ranging between 50% below and 50% 156 // above the retry interval. 157 randomizationFactor float64 158 159 // The value to multiply the current interval with for each retry attempt. 160 multiplier float64 161 162 // The maximum value of the back off period. Once the retry interval reaches this 163 // value it stops increasing. 164 // It takes no effect If maxInterval < 0 165 maxInterval time.Duration 166 167 // The system time in nanoseconds. It is calculated when an ExponentialBackOffPolicy instance is 168 // created and is reset when {@link #reset()} is called. 169 startTime time.Time 170 171 // The maximum elapsed time after instantiating {@link ExponentialBackOff} or calling {@link 172 // #reset()} after which {@link #NextBackOff()} returns {@link BackOff#STOP}. 173 // It takes no effect If maxElapsedDuration < 0 174 maxElapsedDuration time.Duration 175 176 // The maximum elapsed count after instantiating {@link ExponentialBackOff} or calling {@link 177 // #reset()} after which {@link #NextBackOff()} returns {@link BackOff#STOP}. 178 // It takes no effect If maxElapsedCount < 0 179 maxElapsedCount int 180 } 181 182 func (o *ExponentialBackOff) SetDefault() { 183 o.initialInterval = DefaultInitialInterval 184 o.randomizationFactor = DefaultRandomizationFactor 185 o.multiplier = DefaultMultiplier 186 o.maxInterval = DefaultMaxInterval 187 o.maxElapsedDuration = DefaultMaxElapsedDuration 188 o.maxElapsedCount = DefaultMaxElapsedCount 189 } 190 191 // NewExponentialBackOff returns a no limit backoff 192 func NewExponentialBackOff(opts ...ExponentialBackOffOption) *ExponentialBackOff { 193 opts = append([]ExponentialBackOffOption{WithExponentialBackOffOptionNoLimit()}, opts...) 194 o := &ExponentialBackOff{} 195 o.SetDefault() 196 o.ApplyOptions(opts...) 197 o.Reset() 198 return o 199 } 200 201 // NewDefaultExponentialBackOff returns a backoff with default limit 202 func NewDefaultExponentialBackOff(opts ...ExponentialBackOffOption) *ExponentialBackOff { 203 o := &ExponentialBackOff{} 204 o.SetDefault() 205 o.ApplyOptions(opts...) 206 o.Reset() 207 return o 208 } 209 210 // NewGrpcExponentialBackOff is a backoff from configuration with the default values specified 211 // at https://github.com/grpc/grpc/blob/master/doc/connection-backoff.md. 212 // 213 // This should be useful for callers who want to configure backoff with 214 // non-default values only for a subset of the options. 215 func NewGrpcExponentialBackOff(opts ...ExponentialBackOffOption) *ExponentialBackOff { 216 opts = append([]ExponentialBackOffOption{WithExponentialBackOffOptionGRPC()}, opts...) 217 o := &ExponentialBackOff{} 218 o.SetDefault() 219 o.ApplyOptions(opts...) 220 o.Reset() 221 return o 222 } 223 224 // Reset Sets the interval back to the initial retry interval and restarts the timer. 225 func (o *ExponentialBackOff) Reset() { 226 o.currentInterval = o.initialInterval 227 o.currentCount = 0 228 o.startTime = time.Now() 229 } 230 231 // NextBackOff This method calculates the next back off interval using the formula: randomized_interval = 232 // retry_interval +/- (randomization_factor * retry_interval) 233 // Subclasses may override if a different algorithm is required. 234 func (o *ExponentialBackOff) NextBackOff() (backoff time.Duration, ok bool) { 235 // Make sure we have not gone over the maximum elapsed count. 236 if o.maxElapsedCount > 0 && o.GetElapsedCount() >= o.maxElapsedCount { 237 return o.currentInterval, false 238 } 239 240 // Make sure we have not gone over the maximum elapsed time. 241 if o.maxElapsedDuration > 0 && o.GetElapsedDuration() > o.maxElapsedDuration { 242 return o.currentInterval, false 243 } 244 245 randomizedInterval := o.GetRandomValueFromInterval(o.randomizationFactor, o.currentInterval) 246 o.incrementCurrentInterval() 247 o.incrementCurrentCount() 248 return randomizedInterval, true 249 } 250 251 // GetRandomValueFromInterval Returns a random value from the interval 252 // [randomizationFactor * currentInterval, randomizationFactor * currentInterval]. 253 func (o *ExponentialBackOff) GetRandomValueFromInterval( 254 randomizationFactor float64, currentInterval time.Duration) time.Duration { 255 return Jitter(currentInterval, randomizationFactor) 256 } 257 258 // GetInitialInterval Returns the initial retry interval. 259 func (o *ExponentialBackOff) GetInitialInterval() time.Duration { 260 return o.initialInterval 261 } 262 263 // GetRandomizationFactor Returns the randomization factor to use for creating a range around the retry interval. 264 // A randomization factor of 0.5 results in a random period ranging between 50% below and 50% 265 // above the retry interval. 266 func (o *ExponentialBackOff) GetRandomizationFactor() float64 { 267 return o.randomizationFactor 268 } 269 270 // GetCurrentInterval Returns the current retry interval. 271 func (o *ExponentialBackOff) GetCurrentInterval() time.Duration { 272 return o.currentInterval 273 } 274 275 // GetMultiplier Returns the value to multiply the current interval with for each retry attempt. 276 func (o *ExponentialBackOff) GetMultiplier() float64 { 277 return o.multiplier 278 } 279 280 // GetMaxInterval Returns the maximum value of the back off period. Once the current interval 281 // reaches this value it stops increasing. 282 func (o *ExponentialBackOff) GetMaxInterval() time.Duration { 283 return o.maxInterval 284 } 285 286 // GetMaxElapsedDuration Returns the maximum elapsed time. 287 // If the time elapsed since an {@link ExponentialBackOff} instance is created goes past the 288 // max_elapsed_time then the method {@link #NextBackOff()} starts returning STOP. 289 // The elapsed time can be reset by calling 290 func (o *ExponentialBackOff) GetMaxElapsedDuration() time.Duration { 291 return o.maxElapsedDuration 292 } 293 294 // GetElapsedDuration Returns the elapsed time since an {@link ExponentialBackOff} instance is 295 // created and is reset when {@link #reset()} is called. 296 // The elapsed time is computed using {@link System#nanoTime()}. 297 func (o *ExponentialBackOff) GetElapsedDuration() time.Duration { 298 return time.Now().Sub(o.startTime) 299 } 300 301 // GetElapsedCount Returns the elapsed count since an {@link ExponentialBackOff} instance is 302 // created and is reset when {@link #reset()} is called. 303 func (o *ExponentialBackOff) GetElapsedCount() int { 304 return o.currentCount 305 } 306 307 // Increments the current interval by multiplying it with the multiplier. 308 func (o *ExponentialBackOff) incrementCurrentInterval() { 309 // Check for overflow, if overflow is detected set the current interval to the max interval. 310 if o.maxInterval >= 0 && o.currentInterval*time.Duration(o.multiplier) >= o.maxInterval { 311 o.currentInterval = o.maxInterval 312 return 313 } 314 o.currentInterval = time.Duration(float64(o.currentInterval) * o.multiplier) 315 } 316 317 // Increments the current count by ++. 318 func (o *ExponentialBackOff) incrementCurrentCount() { 319 // Check for overflow, if overflow is detected set the current interval to the max interval. 320 if o.currentCount >= math.MaxInt64 { 321 o.currentCount = math.MaxInt64 322 return 323 } 324 o.currentCount++ 325 }