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 }