default_retryer.go 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  1. package client
  2. import (
  3. "math/rand"
  4. "sync"
  5. "time"
  6. "github.com/aws/aws-sdk-go/aws/request"
  7. )
  8. // DefaultRetryer implements basic retry logic using exponential backoff for
  9. // most services. If you want to implement custom retry logic, implement the
  10. // request.Retryer interface or create a structure type that composes this
  11. // struct and override the specific methods. For example, to override only
  12. // the MaxRetries method:
  13. //
  14. // type retryer struct {
  15. // client.DefaultRetryer
  16. // }
  17. //
  18. // // This implementation always has 100 max retries
  19. // func (d retryer) MaxRetries() int { return 100 }
  20. type DefaultRetryer struct {
  21. NumMaxRetries int
  22. }
  23. // MaxRetries returns the number of maximum returns the service will use to make
  24. // an individual API request.
  25. func (d DefaultRetryer) MaxRetries() int {
  26. return d.NumMaxRetries
  27. }
  28. var seededRand = rand.New(&lockedSource{src: rand.NewSource(time.Now().UnixNano())})
  29. // RetryRules returns the delay duration before retrying this request again
  30. func (d DefaultRetryer) RetryRules(r *request.Request) time.Duration {
  31. // Set the upper limit of delay in retrying at ~five minutes
  32. minTime := 30
  33. throttle := d.shouldThrottle(r)
  34. if throttle {
  35. minTime = 500
  36. }
  37. retryCount := r.RetryCount
  38. if retryCount > 13 {
  39. retryCount = 13
  40. } else if throttle && retryCount > 8 {
  41. retryCount = 8
  42. }
  43. delay := (1 << uint(retryCount)) * (seededRand.Intn(minTime) + minTime)
  44. return time.Duration(delay) * time.Millisecond
  45. }
  46. // ShouldRetry returns true if the request should be retried.
  47. func (d DefaultRetryer) ShouldRetry(r *request.Request) bool {
  48. // If one of the other handlers already set the retry state
  49. // we don't want to override it based on the service's state
  50. if r.Retryable != nil {
  51. return *r.Retryable
  52. }
  53. if r.HTTPResponse.StatusCode >= 500 {
  54. return true
  55. }
  56. return r.IsErrorRetryable() || d.shouldThrottle(r)
  57. }
  58. // ShouldThrottle returns true if the request should be throttled.
  59. func (d DefaultRetryer) shouldThrottle(r *request.Request) bool {
  60. if r.HTTPResponse.StatusCode == 502 ||
  61. r.HTTPResponse.StatusCode == 503 ||
  62. r.HTTPResponse.StatusCode == 504 {
  63. return true
  64. }
  65. return r.IsErrorThrottle()
  66. }
  67. // lockedSource is a thread-safe implementation of rand.Source
  68. type lockedSource struct {
  69. lk sync.Mutex
  70. src rand.Source
  71. }
  72. func (r *lockedSource) Int63() (n int64) {
  73. r.lk.Lock()
  74. n = r.src.Int63()
  75. r.lk.Unlock()
  76. return
  77. }
  78. func (r *lockedSource) Seed(seed int64) {
  79. r.lk.Lock()
  80. r.src.Seed(seed)
  81. r.lk.Unlock()
  82. }