1package client 2 3import ( 4 "strconv" 5 "time" 6 7 "github.com/aws/aws-sdk-go/aws/request" 8 "github.com/aws/aws-sdk-go/internal/sdkrand" 9) 10 11// DefaultRetryer implements basic retry logic using exponential backoff for 12// most services. If you want to implement custom retry logic, implement the 13// request.Retryer interface or create a structure type that composes this 14// struct and override the specific methods. For example, to override only 15// the MaxRetries method: 16// 17// type retryer struct { 18// client.DefaultRetryer 19// } 20// 21// // This implementation always has 100 max retries 22// func (d retryer) MaxRetries() int { return 100 } 23type DefaultRetryer struct { 24 NumMaxRetries int 25} 26 27// MaxRetries returns the number of maximum returns the service will use to make 28// an individual API request. 29func (d DefaultRetryer) MaxRetries() int { 30 return d.NumMaxRetries 31} 32 33// RetryRules returns the delay duration before retrying this request again 34func (d DefaultRetryer) RetryRules(r *request.Request) time.Duration { 35 // Set the upper limit of delay in retrying at ~five minutes 36 minTime := 30 37 throttle := d.shouldThrottle(r) 38 if throttle { 39 if delay, ok := getRetryDelay(r); ok { 40 return delay 41 } 42 43 minTime = 500 44 } 45 46 retryCount := r.RetryCount 47 if throttle && retryCount > 8 { 48 retryCount = 8 49 } else if retryCount > 13 { 50 retryCount = 13 51 } 52 53 delay := (1 << uint(retryCount)) * (sdkrand.SeededRand.Intn(minTime) + minTime) 54 return time.Duration(delay) * time.Millisecond 55} 56 57// ShouldRetry returns true if the request should be retried. 58func (d DefaultRetryer) ShouldRetry(r *request.Request) bool { 59 // If one of the other handlers already set the retry state 60 // we don't want to override it based on the service's state 61 if r.Retryable != nil { 62 return *r.Retryable 63 } 64 65 if r.HTTPResponse.StatusCode >= 500 && r.HTTPResponse.StatusCode != 501 { 66 return true 67 } 68 return r.IsErrorRetryable() || d.shouldThrottle(r) 69} 70 71// ShouldThrottle returns true if the request should be throttled. 72func (d DefaultRetryer) shouldThrottle(r *request.Request) bool { 73 switch r.HTTPResponse.StatusCode { 74 case 429: 75 case 502: 76 case 503: 77 case 504: 78 default: 79 return r.IsErrorThrottle() 80 } 81 82 return true 83} 84 85// This will look in the Retry-After header, RFC 7231, for how long 86// it will wait before attempting another request 87func getRetryDelay(r *request.Request) (time.Duration, bool) { 88 if !canUseRetryAfterHeader(r) { 89 return 0, false 90 } 91 92 delayStr := r.HTTPResponse.Header.Get("Retry-After") 93 if len(delayStr) == 0 { 94 return 0, false 95 } 96 97 delay, err := strconv.Atoi(delayStr) 98 if err != nil { 99 return 0, false 100 } 101 102 return time.Duration(delay) * time.Second, true 103} 104 105// Will look at the status code to see if the retry header pertains to 106// the status code. 107func canUseRetryAfterHeader(r *request.Request) bool { 108 switch r.HTTPResponse.StatusCode { 109 case 429: 110 case 503: 111 default: 112 return false 113 } 114 115 return true 116} 117