github.com/imran-kn/cilium-fork@v1.6.9/pkg/backoff/backoff.go (about) 1 // Copyright 2018-2019 Authors of Cilium 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 16 17 import ( 18 "context" 19 "fmt" 20 "math" 21 "math/rand" 22 "time" 23 24 "github.com/cilium/cilium/pkg/logging" 25 "github.com/cilium/cilium/pkg/logging/logfields" 26 "github.com/cilium/cilium/pkg/uuid" 27 28 "github.com/sirupsen/logrus" 29 ) 30 31 var log = logging.DefaultLogger.WithField(logfields.LogSubsys, "backoff") 32 33 // NodeManager is the interface required to implement cluster size dependent 34 // intervals 35 type NodeManager interface { 36 ClusterSizeDependantInterval(baseInterval time.Duration) time.Duration 37 } 38 39 // Exponential implements an exponential backoff 40 type Exponential struct { 41 // Min is the minimal backoff time, if unspecified, 1 second will be 42 // used 43 Min time.Duration 44 45 // Max is the maximum backoff time, if unspecified, no maximum time is 46 // applied 47 Max time.Duration 48 49 // Factor is the factor the backoff time grows exponentially, if 50 // unspecified, a factor of 2.0 will be used 51 Factor float64 52 53 // Jitter, when enabled, adds random jitter to the interval 54 Jitter bool 55 56 // NodeManager enables the use of cluster size dependent backoff 57 // intervals, i.e. the larger the cluster, the longer the backoff 58 // interval 59 NodeManager NodeManager 60 61 // Name is a free form string describing the operation subject to the 62 // backoff, if unspecified, a UUID is generated. This string is used 63 // for logging purposes. 64 Name string 65 66 attempt int 67 } 68 69 // CalculateDuration calculates the backoff duration based on minimum base 70 // interval, exponential factor, jitter and number of failures. 71 func CalculateDuration(min, max time.Duration, factor float64, jitter bool, failures int) time.Duration { 72 minFloat := float64(min) 73 maxFloat := float64(max) 74 75 t := minFloat * math.Pow(factor, float64(failures)) 76 if max != time.Duration(0) && t > maxFloat { 77 t = maxFloat 78 } 79 80 if jitter { 81 t = rand.Float64()*(t-minFloat) + minFloat 82 } 83 84 return time.Duration(t) 85 } 86 87 // Wait waits for the required time using an exponential backoff 88 func (b *Exponential) Wait(ctx context.Context) error { 89 b.attempt++ 90 t := b.Duration(b.attempt) 91 92 log.WithFields(logrus.Fields{ 93 "time": t, 94 "attempt": b.attempt, 95 "name": b.Name, 96 }).Debug("Sleeping with exponential backoff") 97 98 select { 99 case <-ctx.Done(): 100 return fmt.Errorf("exponential backoff cancelled via context: %s", ctx.Err()) 101 case <-time.After(t): 102 } 103 104 return nil 105 } 106 107 // Duration returns the wait duration for the nth attempt 108 func (b *Exponential) Duration(attempt int) time.Duration { 109 if b.Name == "" { 110 b.Name = uuid.NewUUID().String() 111 } 112 113 min := time.Duration(1) * time.Second 114 if b.Min != time.Duration(0) { 115 min = b.Min 116 } 117 118 factor := float64(2) 119 if b.Factor != float64(0) { 120 factor = b.Factor 121 } 122 123 t := CalculateDuration(min, b.Max, factor, b.Jitter, attempt) 124 125 if b.NodeManager != nil { 126 t = b.NodeManager.ClusterSizeDependantInterval(t) 127 } 128 129 if b.Max != time.Duration(0) && t > b.Max { 130 t = b.Max 131 } 132 133 return t 134 }