1package client 2 3import ( 4 "math" 5 "strconv" 6 "time" 7 8 "github.com/aws/aws-sdk-go/aws/request" 9 "github.com/aws/aws-sdk-go/internal/sdkrand" 10) 11 12// DefaultRetryer implements basic retry logic using exponential backoff for 13// most services. If you want to implement custom retry logic, you can implement the 14// request.Retryer interface. 15// 16type DefaultRetryer struct { 17 // Num max Retries is the number of max retries that will be performed. 18 // By default, this is zero. 19 NumMaxRetries int 20 21 // MinRetryDelay is the minimum retry delay after which retry will be performed. 22 // If not set, the value is 0ns. 23 MinRetryDelay time.Duration 24 25 // MinThrottleRetryDelay is the minimum retry delay when throttled. 26 // If not set, the value is 0ns. 27 MinThrottleDelay time.Duration 28 29 // MaxRetryDelay is the maximum retry delay before which retry must be performed. 30 // If not set, the value is 0ns. 31 MaxRetryDelay time.Duration 32 33 // MaxThrottleDelay is the maximum retry delay when throttled. 34 // If not set, the value is 0ns. 35 MaxThrottleDelay time.Duration 36} 37 38const ( 39 // DefaultRetryerMaxNumRetries sets maximum number of retries 40 DefaultRetryerMaxNumRetries = 3 41 42 // DefaultRetryerMinRetryDelay sets minimum retry delay 43 DefaultRetryerMinRetryDelay = 30 * time.Millisecond 44 45 // DefaultRetryerMinThrottleDelay sets minimum delay when throttled 46 DefaultRetryerMinThrottleDelay = 500 * time.Millisecond 47 48 // DefaultRetryerMaxRetryDelay sets maximum retry delay 49 DefaultRetryerMaxRetryDelay = 300 * time.Second 50 51 // DefaultRetryerMaxThrottleDelay sets maximum delay when throttled 52 DefaultRetryerMaxThrottleDelay = 300 * time.Second 53) 54 55// MaxRetries returns the number of maximum returns the service will use to make 56// an individual API request. 57func (d DefaultRetryer) MaxRetries() int { 58 return d.NumMaxRetries 59} 60 61// setRetryerDefaults sets the default values of the retryer if not set 62func (d *DefaultRetryer) setRetryerDefaults() { 63 if d.MinRetryDelay == 0 { 64 d.MinRetryDelay = DefaultRetryerMinRetryDelay 65 } 66 if d.MaxRetryDelay == 0 { 67 d.MaxRetryDelay = DefaultRetryerMaxRetryDelay 68 } 69 if d.MinThrottleDelay == 0 { 70 d.MinThrottleDelay = DefaultRetryerMinThrottleDelay 71 } 72 if d.MaxThrottleDelay == 0 { 73 d.MaxThrottleDelay = DefaultRetryerMaxThrottleDelay 74 } 75} 76 77// RetryRules returns the delay duration before retrying this request again 78func (d DefaultRetryer) RetryRules(r *request.Request) time.Duration { 79 80 // if number of max retries is zero, no retries will be performed. 81 if d.NumMaxRetries == 0 { 82 return 0 83 } 84 85 // Sets default value for retryer members 86 d.setRetryerDefaults() 87 88 // minDelay is the minimum retryer delay 89 minDelay := d.MinRetryDelay 90 91 var initialDelay time.Duration 92 93 isThrottle := r.IsErrorThrottle() 94 if isThrottle { 95 if delay, ok := getRetryAfterDelay(r); ok { 96 initialDelay = delay 97 } 98 minDelay = d.MinThrottleDelay 99 } 100 101 retryCount := r.RetryCount 102 103 // maxDelay the maximum retryer delay 104 maxDelay := d.MaxRetryDelay 105 106 if isThrottle { 107 maxDelay = d.MaxThrottleDelay 108 } 109 110 var delay time.Duration 111 112 // Logic to cap the retry count based on the minDelay provided 113 actualRetryCount := int(math.Log2(float64(minDelay))) + 1 114 if actualRetryCount < 63-retryCount { 115 delay = time.Duration(1<<uint64(retryCount)) * getJitterDelay(minDelay) 116 if delay > maxDelay { 117 delay = getJitterDelay(maxDelay / 2) 118 } 119 } else { 120 delay = getJitterDelay(maxDelay / 2) 121 } 122 return delay + initialDelay 123} 124 125// getJitterDelay returns a jittered delay for retry 126func getJitterDelay(duration time.Duration) time.Duration { 127 return time.Duration(sdkrand.SeededRand.Int63n(int64(duration)) + int64(duration)) 128} 129 130// ShouldRetry returns true if the request should be retried. 131func (d DefaultRetryer) ShouldRetry(r *request.Request) bool { 132 133 // ShouldRetry returns false if number of max retries is 0. 134 if d.NumMaxRetries == 0 { 135 return false 136 } 137 138 // If one of the other handlers already set the retry state 139 // we don't want to override it based on the service's state 140 if r.Retryable != nil { 141 return *r.Retryable 142 } 143 return r.IsErrorRetryable() || r.IsErrorThrottle() 144} 145 146// This will look in the Retry-After header, RFC 7231, for how long 147// it will wait before attempting another request 148func getRetryAfterDelay(r *request.Request) (time.Duration, bool) { 149 if !canUseRetryAfterHeader(r) { 150 return 0, false 151 } 152 153 delayStr := r.HTTPResponse.Header.Get("Retry-After") 154 if len(delayStr) == 0 { 155 return 0, false 156 } 157 158 delay, err := strconv.Atoi(delayStr) 159 if err != nil { 160 return 0, false 161 } 162 163 return time.Duration(delay) * time.Second, true 164} 165 166// Will look at the status code to see if the retry header pertains to 167// the status code. 168func canUseRetryAfterHeader(r *request.Request) bool { 169 switch r.HTTPResponse.StatusCode { 170 case 429: 171 case 503: 172 default: 173 return false 174 } 175 176 return true 177} 178