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