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 18package minio 19 20import ( 21 "context" 22 "net/http" 23 "time" 24) 25 26// MaxRetry is the maximum number of retries before stopping. 27var MaxRetry = 10 28 29// MaxJitter will randomize over the full exponential backoff time 30const MaxJitter = 1.0 31 32// NoJitter disables the use of jitter for randomizing the exponential backoff time 33const NoJitter = 0.0 34 35// DefaultRetryUnit - default unit multiplicative per retry. 36// defaults to 200 * time.Millisecond 37var DefaultRetryUnit = 200 * time.Millisecond 38 39// DefaultRetryCap - Each retry attempt never waits no longer than 40// this maximum time duration. 41var DefaultRetryCap = time.Second 42 43// newRetryTimer creates a timer with exponentially increasing 44// delays until the maximum retry attempts are reached. 45func (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. 90var 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. 105func isS3CodeRetryable(s3Code string) (ok bool) { 106 _, ok = retryableS3Codes[s3Code] 107 return ok 108} 109 110// List of HTTP status codes which are retryable. 111var 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. 121func isHTTPStatusRetryable(httpStatusCode int) (ok bool) { 122 _, ok = retryableHTTPStatusCodes[httpStatusCode] 123 return ok 124} 125