storj.io/minio@v0.0.0-20230509071714-0cbc90f649b1/pkg/madmin/retry.go (about) 1 /* 2 * MinIO Cloud Storage, (C) 2019-2020 MinIO, Inc. 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 18 package madmin 19 20 import ( 21 "context" 22 "math/rand" 23 "net/http" 24 "sync" 25 "time" 26 ) 27 28 // MaxRetry is the maximum number of retries before stopping. 29 var MaxRetry = 10 30 31 // MaxJitter will randomize over the full exponential backoff time 32 const MaxJitter = 1.0 33 34 // NoJitter disables the use of jitter for randomizing the exponential backoff time 35 const NoJitter = 0.0 36 37 // DefaultRetryUnit - default unit multiplicative per retry. 38 // defaults to 1 second. 39 const DefaultRetryUnit = time.Second 40 41 // DefaultRetryCap - Each retry attempt never waits no longer than 42 // this maximum time duration. 43 const DefaultRetryCap = time.Second * 30 44 45 // lockedRandSource provides protected rand source, implements rand.Source interface. 46 type lockedRandSource struct { 47 lk sync.Mutex 48 src rand.Source 49 } 50 51 // Int63 returns a non-negative pseudo-random 63-bit integer as an int64. 52 func (r *lockedRandSource) Int63() (n int64) { 53 r.lk.Lock() 54 n = r.src.Int63() 55 r.lk.Unlock() 56 return 57 } 58 59 // Seed uses the provided seed value to initialize the generator to a 60 // deterministic state. 61 func (r *lockedRandSource) Seed(seed int64) { 62 r.lk.Lock() 63 r.src.Seed(seed) 64 r.lk.Unlock() 65 } 66 67 // newRetryTimer creates a timer with exponentially increasing 68 // delays until the maximum retry attempts are reached. 69 func (adm AdminClient) newRetryTimer(ctx context.Context, maxRetry int, unit time.Duration, cap time.Duration, jitter float64) <-chan int { 70 attemptCh := make(chan int) 71 72 // computes the exponential backoff duration according to 73 // https://www.awsarchitectureblog.com/2015/03/backoff.html 74 exponentialBackoffWait := func(attempt int) time.Duration { 75 // normalize jitter to the range [0, 1.0] 76 if jitter < NoJitter { 77 jitter = NoJitter 78 } 79 if jitter > MaxJitter { 80 jitter = MaxJitter 81 } 82 83 //sleep = random_between(0, min(cap, base * 2 ** attempt)) 84 sleep := unit * 1 << uint(attempt) 85 if sleep > cap { 86 sleep = cap 87 } 88 if jitter > NoJitter { 89 sleep -= time.Duration(adm.random.Float64() * float64(sleep) * jitter) 90 } 91 return sleep 92 } 93 94 go func() { 95 defer close(attemptCh) 96 for i := 0; i < maxRetry; i++ { 97 // Attempts start from 1. 98 select { 99 case attemptCh <- i + 1: 100 case <-ctx.Done(): 101 // Stop the routine. 102 return 103 } 104 105 select { 106 case <-time.After(exponentialBackoffWait(i)): 107 case <-ctx.Done(): 108 // Stop the routine. 109 return 110 } 111 } 112 }() 113 return attemptCh 114 } 115 116 // List of AWS S3 error codes which are retryable. 117 var retryableS3Codes = map[string]struct{}{ 118 "RequestError": {}, 119 "RequestTimeout": {}, 120 "Throttling": {}, 121 "ThrottlingException": {}, 122 "RequestLimitExceeded": {}, 123 "RequestThrottled": {}, 124 "InternalError": {}, 125 "SlowDown": {}, 126 // Add more AWS S3 codes here. 127 } 128 129 // isS3CodeRetryable - is s3 error code retryable. 130 func isS3CodeRetryable(s3Code string) (ok bool) { 131 _, ok = retryableS3Codes[s3Code] 132 return ok 133 } 134 135 // List of HTTP status codes which are retryable. 136 var retryableHTTPStatusCodes = map[int]struct{}{ 137 http.StatusRequestTimeout: {}, 138 http.StatusTooManyRequests: {}, 139 http.StatusInternalServerError: {}, 140 http.StatusBadGateway: {}, 141 http.StatusServiceUnavailable: {}, 142 // Add more HTTP status codes here. 143 } 144 145 // isHTTPStatusRetryable - is HTTP error code retryable. 146 func isHTTPStatusRetryable(httpStatusCode int) (ok bool) { 147 _, ok = retryableHTTPStatusCodes[httpStatusCode] 148 return ok 149 }