github.com/minio/minio-go/v6@v6.0.57/retry.go (about)

     1  /*
     2   * MinIO Go Library for Amazon S3 Compatible Cloud Storage
     3   * Copyright 2015-2017 MinIO, Inc.
     4   *
     5   * Licensed under the Apache License, Version 2.0 (the "License");
     6   * you may not use this file except in compliance with the License.
     7   * You may obtain a copy of the License at
     8   *
     9   *     http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   * Unless required by applicable law or agreed to in writing, software
    12   * distributed under the License is distributed on an "AS IS" BASIS,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the License for the specific language governing permissions and
    15   * limitations under the License.
    16   */
    17  
    18  package minio
    19  
    20  import (
    21  	"context"
    22  	"net/http"
    23  	"time"
    24  )
    25  
    26  // MaxRetry is the maximum number of retries before stopping.
    27  var MaxRetry = 10
    28  
    29  // MaxJitter will randomize over the full exponential backoff time
    30  const MaxJitter = 1.0
    31  
    32  // NoJitter disables the use of jitter for randomizing the exponential backoff time
    33  const NoJitter = 0.0
    34  
    35  // DefaultRetryUnit - default unit multiplicative per retry.
    36  // defaults to 1 second.
    37  const DefaultRetryUnit = time.Second
    38  
    39  // DefaultRetryCap - Each retry attempt never waits no longer than
    40  // this maximum time duration.
    41  const DefaultRetryCap = time.Second * 30
    42  
    43  // newRetryTimer creates a timer with exponentially increasing
    44  // delays until the maximum retry attempts are reached.
    45  func (c Client) newRetryTimer(ctx context.Context, maxRetry int, unit time.Duration, cap time.Duration, jitter float64) <-chan int {
    46  	attemptCh := make(chan int)
    47  
    48  	// computes the exponential backoff duration according to
    49  	// https://www.awsarchitectureblog.com/2015/03/backoff.html
    50  	exponentialBackoffWait := func(attempt int) time.Duration {
    51  		// normalize jitter to the range [0, 1.0]
    52  		if jitter < NoJitter {
    53  			jitter = NoJitter
    54  		}
    55  		if jitter > MaxJitter {
    56  			jitter = MaxJitter
    57  		}
    58  
    59  		//sleep = random_between(0, min(cap, base * 2 ** attempt))
    60  		sleep := unit * time.Duration(1<<uint(attempt))
    61  		if sleep > cap {
    62  			sleep = cap
    63  		}
    64  		if jitter != NoJitter {
    65  			sleep -= time.Duration(c.random.Float64() * float64(sleep) * jitter)
    66  		}
    67  		return sleep
    68  	}
    69  
    70  	go func() {
    71  		defer close(attemptCh)
    72  		for i := 0; i < maxRetry; i++ {
    73  			select {
    74  			case attemptCh <- i + 1:
    75  			case <-ctx.Done():
    76  				return
    77  			}
    78  
    79  			select {
    80  			case <-time.After(exponentialBackoffWait(i)):
    81  			case <-ctx.Done():
    82  				return
    83  			}
    84  		}
    85  	}()
    86  	return attemptCh
    87  }
    88  
    89  // List of AWS S3 error codes which are retryable.
    90  var retryableS3Codes = map[string]struct{}{
    91  	"RequestError":          {},
    92  	"RequestTimeout":        {},
    93  	"Throttling":            {},
    94  	"ThrottlingException":   {},
    95  	"RequestLimitExceeded":  {},
    96  	"RequestThrottled":      {},
    97  	"InternalError":         {},
    98  	"ExpiredToken":          {},
    99  	"ExpiredTokenException": {},
   100  	"SlowDown":              {},
   101  	// Add more AWS S3 codes here.
   102  }
   103  
   104  // isS3CodeRetryable - is s3 error code retryable.
   105  func isS3CodeRetryable(s3Code string) (ok bool) {
   106  	_, ok = retryableS3Codes[s3Code]
   107  	return ok
   108  }
   109  
   110  // List of HTTP status codes which are retryable.
   111  var retryableHTTPStatusCodes = map[int]struct{}{
   112  	429:                            {}, // http.StatusTooManyRequests is not part of the Go 1.5 library, yet
   113  	http.StatusInternalServerError: {},
   114  	http.StatusBadGateway:          {},
   115  	http.StatusServiceUnavailable:  {},
   116  	http.StatusGatewayTimeout:      {},
   117  	// Add more HTTP status codes here.
   118  }
   119  
   120  // isHTTPStatusRetryable - is HTTP error code retryable.
   121  func isHTTPStatusRetryable(httpStatusCode int) (ok bool) {
   122  	_, ok = retryableHTTPStatusCodes[httpStatusCode]
   123  	return ok
   124  }