k8s.io/kubernetes@v1.29.3/pkg/util/goroutinemap/exponentialbackoff/exponential_backoff.go (about) 1 /* 2 Copyright 2016 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 // Package exponentialbackoff contains logic for implementing exponential 18 // backoff for GoRoutineMap and NestedPendingOperations. 19 package exponentialbackoff 20 21 import ( 22 "fmt" 23 "time" 24 ) 25 26 const ( 27 // initialDurationBeforeRetry is the amount of time after an error occurs 28 // that GoroutineMap will refuse to allow another operation to start with 29 // the same target (if exponentialBackOffOnError is enabled). Each 30 // successive error results in a wait 2x times the previous. 31 initialDurationBeforeRetry = 500 * time.Millisecond 32 33 // maxDurationBeforeRetry is the maximum amount of time that 34 // durationBeforeRetry will grow to due to exponential backoff. 35 // Value is slightly offset from 2 minutes to make timeouts due to this 36 // constant recognizable. 37 maxDurationBeforeRetry = 2*time.Minute + 2*time.Second 38 ) 39 40 // ExponentialBackoff contains the last occurrence of an error and the duration 41 // that retries are not permitted. 42 type ExponentialBackoff struct { 43 lastError error 44 lastErrorTime time.Time 45 durationBeforeRetry time.Duration 46 } 47 48 // SafeToRetry returns an error if the durationBeforeRetry period for the given 49 // lastErrorTime has not yet expired. Otherwise it returns nil. 50 func (expBackoff *ExponentialBackoff) SafeToRetry(operationName string) error { 51 if time.Since(expBackoff.lastErrorTime) <= expBackoff.durationBeforeRetry { 52 return NewExponentialBackoffError(operationName, *expBackoff) 53 } 54 55 return nil 56 } 57 58 func (expBackoff *ExponentialBackoff) Update(err *error) { 59 if expBackoff.durationBeforeRetry == 0 { 60 expBackoff.durationBeforeRetry = initialDurationBeforeRetry 61 } else { 62 expBackoff.durationBeforeRetry = 2 * expBackoff.durationBeforeRetry 63 if expBackoff.durationBeforeRetry > maxDurationBeforeRetry { 64 expBackoff.durationBeforeRetry = maxDurationBeforeRetry 65 } 66 } 67 68 expBackoff.lastError = *err 69 expBackoff.lastErrorTime = time.Now() 70 } 71 72 func (expBackoff *ExponentialBackoff) GenerateNoRetriesPermittedMsg(operationName string) string { 73 return fmt.Sprintf("Operation for %q failed. No retries permitted until %v (durationBeforeRetry %v). Error: %v", 74 operationName, 75 expBackoff.lastErrorTime.Add(expBackoff.durationBeforeRetry), 76 expBackoff.durationBeforeRetry, 77 expBackoff.lastError) 78 } 79 80 // NewExponentialBackoffError returns a new instance of ExponentialBackoff error. 81 func NewExponentialBackoffError( 82 operationName string, expBackoff ExponentialBackoff) error { 83 return exponentialBackoffError{ 84 operationName: operationName, 85 expBackoff: expBackoff, 86 } 87 } 88 89 // IsExponentialBackoff returns true if an error returned from GoroutineMap 90 // indicates that a new operation can not be started because 91 // exponentialBackOffOnError is enabled and a previous operation with the same 92 // operation failed within the durationBeforeRetry period. 93 func IsExponentialBackoff(err error) bool { 94 switch err.(type) { 95 case exponentialBackoffError: 96 return true 97 default: 98 return false 99 } 100 } 101 102 // exponentialBackoffError is the error returned returned from GoroutineMap when 103 // a new operation can not be started because exponentialBackOffOnError is 104 // enabled and a previous operation with the same operation failed within the 105 // durationBeforeRetry period. 106 type exponentialBackoffError struct { 107 operationName string 108 expBackoff ExponentialBackoff 109 } 110 111 var _ error = exponentialBackoffError{} 112 113 func (err exponentialBackoffError) Error() string { 114 return fmt.Sprintf( 115 "Failed to create operation with name %q. An operation with that name failed at %v. No retries permitted until %v (%v). Last error: %q.", 116 err.operationName, 117 err.expBackoff.lastErrorTime, 118 err.expBackoff.lastErrorTime.Add(err.expBackoff.durationBeforeRetry), 119 err.expBackoff.durationBeforeRetry, 120 err.expBackoff.lastError) 121 }