retryer.go 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. package request
  2. import (
  3. "time"
  4. "github.com/aws/aws-sdk-go/aws"
  5. "github.com/aws/aws-sdk-go/aws/awserr"
  6. )
  7. // Retryer is an interface to control retry logic for a given service.
  8. // The default implementation used by most services is the client.DefaultRetryer
  9. // structure, which contains basic retry logic using exponential backoff.
  10. type Retryer interface {
  11. RetryRules(*Request) time.Duration
  12. ShouldRetry(*Request) bool
  13. MaxRetries() int
  14. }
  15. // WithRetryer sets a config Retryer value to the given Config returning it
  16. // for chaining.
  17. func WithRetryer(cfg *aws.Config, retryer Retryer) *aws.Config {
  18. cfg.Retryer = retryer
  19. return cfg
  20. }
  21. // retryableCodes is a collection of service response codes which are retry-able
  22. // without any further action.
  23. var retryableCodes = map[string]struct{}{
  24. "RequestError": {},
  25. "RequestTimeout": {},
  26. ErrCodeResponseTimeout: {},
  27. "RequestTimeoutException": {}, // Glacier's flavor of RequestTimeout
  28. }
  29. var throttleCodes = map[string]struct{}{
  30. "ProvisionedThroughputExceededException": {},
  31. "Throttling": {},
  32. "ThrottlingException": {},
  33. "RequestLimitExceeded": {},
  34. "RequestThrottled": {},
  35. "TooManyRequestsException": {}, // Lambda functions
  36. "PriorRequestNotComplete": {}, // Route53
  37. "TransactionInProgressException": {},
  38. }
  39. // credsExpiredCodes is a collection of error codes which signify the credentials
  40. // need to be refreshed. Expired tokens require refreshing of credentials, and
  41. // resigning before the request can be retried.
  42. var credsExpiredCodes = map[string]struct{}{
  43. "ExpiredToken": {},
  44. "ExpiredTokenException": {},
  45. "RequestExpired": {}, // EC2 Only
  46. }
  47. func isCodeThrottle(code string) bool {
  48. _, ok := throttleCodes[code]
  49. return ok
  50. }
  51. func isCodeRetryable(code string) bool {
  52. if _, ok := retryableCodes[code]; ok {
  53. return true
  54. }
  55. return isCodeExpiredCreds(code)
  56. }
  57. func isCodeExpiredCreds(code string) bool {
  58. _, ok := credsExpiredCodes[code]
  59. return ok
  60. }
  61. var validParentCodes = map[string]struct{}{
  62. ErrCodeSerialization: {},
  63. ErrCodeRead: {},
  64. }
  65. type temporaryError interface {
  66. Temporary() bool
  67. }
  68. func isNestedErrorRetryable(parentErr awserr.Error) bool {
  69. if parentErr == nil {
  70. return false
  71. }
  72. if _, ok := validParentCodes[parentErr.Code()]; !ok {
  73. return false
  74. }
  75. err := parentErr.OrigErr()
  76. if err == nil {
  77. return false
  78. }
  79. if aerr, ok := err.(awserr.Error); ok {
  80. return isCodeRetryable(aerr.Code())
  81. }
  82. if t, ok := err.(temporaryError); ok {
  83. return t.Temporary() || isErrConnectionReset(err)
  84. }
  85. return isErrConnectionReset(err)
  86. }
  87. // IsErrorRetryable returns whether the error is retryable, based on its Code.
  88. // Returns false if error is nil.
  89. func IsErrorRetryable(err error) bool {
  90. if err != nil {
  91. if aerr, ok := err.(awserr.Error); ok {
  92. return isCodeRetryable(aerr.Code()) || isNestedErrorRetryable(aerr)
  93. }
  94. }
  95. return false
  96. }
  97. // IsErrorThrottle returns whether the error is to be throttled based on its code.
  98. // Returns false if error is nil.
  99. func IsErrorThrottle(err error) bool {
  100. if err != nil {
  101. if aerr, ok := err.(awserr.Error); ok {
  102. return isCodeThrottle(aerr.Code())
  103. }
  104. }
  105. return false
  106. }
  107. // IsErrorExpiredCreds returns whether the error code is a credential expiry error.
  108. // Returns false if error is nil.
  109. func IsErrorExpiredCreds(err error) bool {
  110. if err != nil {
  111. if aerr, ok := err.(awserr.Error); ok {
  112. return isCodeExpiredCreds(aerr.Code())
  113. }
  114. }
  115. return false
  116. }
  117. // IsErrorRetryable returns whether the error is retryable, based on its Code.
  118. // Returns false if the request has no Error set.
  119. //
  120. // Alias for the utility function IsErrorRetryable
  121. func (r *Request) IsErrorRetryable() bool {
  122. return IsErrorRetryable(r.Error)
  123. }
  124. // IsErrorThrottle returns whether the error is to be throttled based on its code.
  125. // Returns false if the request has no Error set
  126. //
  127. // Alias for the utility function IsErrorThrottle
  128. func (r *Request) IsErrorThrottle() bool {
  129. return IsErrorThrottle(r.Error)
  130. }
  131. // IsErrorExpired returns whether the error code is a credential expiry error.
  132. // Returns false if the request has no Error set.
  133. //
  134. // Alias for the utility function IsErrorExpiredCreds
  135. func (r *Request) IsErrorExpired() bool {
  136. return IsErrorExpiredCreds(r.Error)
  137. }