...

Source file src/k8s.io/kubernetes/pkg/util/goroutinemap/exponentialbackoff/exponential_backoff.go

Documentation: k8s.io/kubernetes/pkg/util/goroutinemap/exponentialbackoff

     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  }
   122  

View as plain text