github.com/zorawar87/trillian@v1.2.1/client/backoff/backoff.go (about) 1 // Copyright 2017 Google Inc. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Package backoff allows retrying an operation with backoff. 16 package backoff 17 18 import ( 19 "context" 20 "math/rand" 21 "time" 22 ) 23 24 // Backoff specifies the parameters of the backoff algorithm. Works correctly 25 // if 0 < Min <= Max <= 2^62 (nanosec), and Factor >= 1. 26 type Backoff struct { 27 Min time.Duration // Duration of the first pause. 28 Max time.Duration // Max duration of a pause. 29 Factor float64 // The factor of duration increase between iterations. 30 Jitter bool // Add random noise to pauses. 31 32 delta time.Duration // Current pause duration relative to Min, no jitter. 33 } 34 35 // Duration returns the time to wait on current retry iteration. Every time 36 // Duration is called, the returned value will exponentially increase by Factor 37 // until Backoff.Max. If Jitter is enabled, will add an additional random value 38 // between 0 and the duration, so the result can at most double. 39 func (b *Backoff) Duration() time.Duration { 40 base := b.Min + b.delta 41 pause := base 42 if b.Jitter { // Add a number in the range [0, pause). 43 pause += time.Duration(rand.Int63n(int64(pause))) 44 } 45 46 nextPause := time.Duration(float64(base) * b.Factor) 47 if nextPause > b.Max || nextPause < b.Min { // Multiplication could overflow. 48 nextPause = b.Max 49 } 50 b.delta = nextPause - b.Min 51 52 return pause 53 } 54 55 // Reset sets the internal state back to first retry iteration. 56 func (b *Backoff) Reset() { 57 b.delta = 0 58 } 59 60 // Retry calls a function until it succeeds or the context is done. 61 // It will backoff if the function returns an error. 62 // Once the context is done, retries will end and the most recent error will be returned. 63 // Backoff is not reset by this function. 64 func (b *Backoff) Retry(ctx context.Context, f func() error) error { 65 // If the context is already done, don't make any attempts to call f. 66 if ctx.Err() != nil { 67 return ctx.Err() 68 } 69 70 // Try calling f until it doesn't return an error or ctx is done. 71 for { 72 if err := f(); err != nil { 73 select { 74 case <-time.After(b.Duration()): 75 continue 76 case <-ctx.Done(): 77 return err 78 } 79 } 80 return nil 81 } 82 }