gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

retry.go (4270B)


      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 	"crypto/x509"
     23 	"errors"
     24 	"net/http"
     25 	"net/url"
     26 	"time"
     27 )
     28 
     29 // MaxRetry is the maximum number of retries before stopping.
     30 var MaxRetry = 10
     31 
     32 // MaxJitter will randomize over the full exponential backoff time
     33 const MaxJitter = 1.0
     34 
     35 // NoJitter disables the use of jitter for randomizing the exponential backoff time
     36 const NoJitter = 0.0
     37 
     38 // DefaultRetryUnit - default unit multiplicative per retry.
     39 // defaults to 200 * time.Millisecond
     40 var DefaultRetryUnit = 200 * time.Millisecond
     41 
     42 // DefaultRetryCap - Each retry attempt never waits no longer than
     43 // this maximum time duration.
     44 var DefaultRetryCap = time.Second
     45 
     46 // newRetryTimer creates a timer with exponentially increasing
     47 // delays until the maximum retry attempts are reached.
     48 func (c *Client) newRetryTimer(ctx context.Context, maxRetry int, unit time.Duration, cap time.Duration, jitter float64) <-chan int {
     49 	attemptCh := make(chan int)
     50 
     51 	// computes the exponential backoff duration according to
     52 	// https://www.awsarchitectureblog.com/2015/03/backoff.html
     53 	exponentialBackoffWait := func(attempt int) time.Duration {
     54 		// normalize jitter to the range [0, 1.0]
     55 		if jitter < NoJitter {
     56 			jitter = NoJitter
     57 		}
     58 		if jitter > MaxJitter {
     59 			jitter = MaxJitter
     60 		}
     61 
     62 		// sleep = random_between(0, min(cap, base * 2 ** attempt))
     63 		sleep := unit * time.Duration(1<<uint(attempt))
     64 		if sleep > cap {
     65 			sleep = cap
     66 		}
     67 		if jitter != NoJitter {
     68 			sleep -= time.Duration(c.random.Float64() * float64(sleep) * jitter)
     69 		}
     70 		return sleep
     71 	}
     72 
     73 	go func() {
     74 		defer close(attemptCh)
     75 		for i := 0; i < maxRetry; i++ {
     76 			select {
     77 			case attemptCh <- i + 1:
     78 			case <-ctx.Done():
     79 				return
     80 			}
     81 
     82 			select {
     83 			case <-time.After(exponentialBackoffWait(i)):
     84 			case <-ctx.Done():
     85 				return
     86 			}
     87 		}
     88 	}()
     89 	return attemptCh
     90 }
     91 
     92 // List of AWS S3 error codes which are retryable.
     93 var retryableS3Codes = map[string]struct{}{
     94 	"RequestError":          {},
     95 	"RequestTimeout":        {},
     96 	"Throttling":            {},
     97 	"ThrottlingException":   {},
     98 	"RequestLimitExceeded":  {},
     99 	"RequestThrottled":      {},
    100 	"InternalError":         {},
    101 	"ExpiredToken":          {},
    102 	"ExpiredTokenException": {},
    103 	"SlowDown":              {},
    104 	// Add more AWS S3 codes here.
    105 }
    106 
    107 // isS3CodeRetryable - is s3 error code retryable.
    108 func isS3CodeRetryable(s3Code string) (ok bool) {
    109 	_, ok = retryableS3Codes[s3Code]
    110 	return ok
    111 }
    112 
    113 // List of HTTP status codes which are retryable.
    114 var retryableHTTPStatusCodes = map[int]struct{}{
    115 	429:                            {}, // http.StatusTooManyRequests is not part of the Go 1.5 library, yet
    116 	499:                            {}, // client closed request, retry. A non-standard status code introduced by nginx.
    117 	http.StatusInternalServerError: {},
    118 	http.StatusBadGateway:          {},
    119 	http.StatusServiceUnavailable:  {},
    120 	http.StatusGatewayTimeout:      {},
    121 	// Add more HTTP status codes here.
    122 }
    123 
    124 // isHTTPStatusRetryable - is HTTP error code retryable.
    125 func isHTTPStatusRetryable(httpStatusCode int) (ok bool) {
    126 	_, ok = retryableHTTPStatusCodes[httpStatusCode]
    127 	return ok
    128 }
    129 
    130 // For now, all http Do() requests are retriable except some well defined errors
    131 func isRequestErrorRetryable(err error) bool {
    132 	if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) {
    133 		return false
    134 	}
    135 	if ue, ok := err.(*url.Error); ok {
    136 		e := ue.Unwrap()
    137 		switch e.(type) {
    138 		// x509: certificate signed by unknown authority
    139 		case x509.UnknownAuthorityError:
    140 			return false
    141 		}
    142 		switch e.Error() {
    143 		case "http: server gave HTTP response to HTTPS client":
    144 			return false
    145 		}
    146 	}
    147 	return true
    148 }