package client import ( "strconv" "time" "github.com/aws/aws-sdk-go/aws/request" "github.com/aws/aws-sdk-go/internal/sdkrand" ) // DefaultRetryer implements basic retry logic using exponential backoff for // most services. If you want to implement custom retry logic, implement the // request.Retryer interface or create a structure type that composes this // struct and override the specific methods. For example, to override only // the MaxRetries method: // // type retryer struct { // client.DefaultRetryer // } // // // This implementation always has 100 max retries // func (d retryer) MaxRetries() int { return 100 } type DefaultRetryer struct { NumMaxRetries int } // MaxRetries returns the number of maximum returns the service will use to make // an individual API request. func (d DefaultRetryer) MaxRetries() int { return d.NumMaxRetries } // RetryRules returns the delay duration before retrying this request again func (d DefaultRetryer) RetryRules(r *request.Request) time.Duration { // Set the upper limit of delay in retrying at ~five minutes minTime := 30 throttle := d.shouldThrottle(r) if throttle { if delay, ok := getRetryDelay(r); ok { return delay } minTime = 500 } retryCount := r.RetryCount if throttle && retryCount > 8 { retryCount = 8 } else if retryCount > 13 { retryCount = 13 } delay := (1 << uint(retryCount)) * (sdkrand.SeededRand.Intn(minTime) + minTime) return time.Duration(delay) * time.Millisecond } // ShouldRetry returns true if the request should be retried. func (d DefaultRetryer) ShouldRetry(r *request.Request) bool { // If one of the other handlers already set the retry state // we don't want to override it based on the service's state if r.Retryable != nil { return *r.Retryable } if r.HTTPResponse.StatusCode >= 500 && r.HTTPResponse.StatusCode != 501 { return true } return r.IsErrorRetryable() || d.shouldThrottle(r) } // ShouldThrottle returns true if the request should be throttled. func (d DefaultRetryer) shouldThrottle(r *request.Request) bool { switch r.HTTPResponse.StatusCode { case 429: case 502: case 503: case 504: default: return r.IsErrorThrottle() } return true } // This will look in the Retry-After header, RFC 7231, for how long // it will wait before attempting another request func getRetryDelay(r *request.Request) (time.Duration, bool) { if !canUseRetryAfterHeader(r) { return 0, false } delayStr := r.HTTPResponse.Header.Get("Retry-After") if len(delayStr) == 0 { return 0, false } delay, err := strconv.Atoi(delayStr) if err != nil { return 0, false } return time.Duration(delay) * time.Second, true } // Will look at the status code to see if the retry header pertains to // the status code. func canUseRetryAfterHeader(r *request.Request) bool { switch r.HTTPResponse.StatusCode { case 429: case 503: default: return false } return true }