retry.go 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. /*
  2. * MinIO Go Library for Amazon S3 Compatible Cloud Storage
  3. * Copyright 2015-2017 MinIO, Inc.
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. package minio
  18. import (
  19. "context"
  20. "net/http"
  21. "time"
  22. )
  23. // MaxRetry is the maximum number of retries before stopping.
  24. var MaxRetry = 10
  25. // MaxJitter will randomize over the full exponential backoff time
  26. const MaxJitter = 1.0
  27. // NoJitter disables the use of jitter for randomizing the exponential backoff time
  28. const NoJitter = 0.0
  29. // DefaultRetryUnit - default unit multiplicative per retry.
  30. // defaults to 200 * time.Millisecond
  31. var DefaultRetryUnit = 200 * time.Millisecond
  32. // DefaultRetryCap - Each retry attempt never waits no longer than
  33. // this maximum time duration.
  34. var DefaultRetryCap = time.Second
  35. // newRetryTimer creates a timer with exponentially increasing
  36. // delays until the maximum retry attempts are reached.
  37. func (c Client) newRetryTimer(ctx context.Context, maxRetry int, unit time.Duration, cap time.Duration, jitter float64) <-chan int {
  38. attemptCh := make(chan int)
  39. // computes the exponential backoff duration according to
  40. // https://www.awsarchitectureblog.com/2015/03/backoff.html
  41. exponentialBackoffWait := func(attempt int) time.Duration {
  42. // normalize jitter to the range [0, 1.0]
  43. if jitter < NoJitter {
  44. jitter = NoJitter
  45. }
  46. if jitter > MaxJitter {
  47. jitter = MaxJitter
  48. }
  49. //sleep = random_between(0, min(cap, base * 2 ** attempt))
  50. sleep := unit * time.Duration(1<<uint(attempt))
  51. if sleep > cap {
  52. sleep = cap
  53. }
  54. if jitter != NoJitter {
  55. sleep -= time.Duration(c.random.Float64() * float64(sleep) * jitter)
  56. }
  57. return sleep
  58. }
  59. go func() {
  60. defer close(attemptCh)
  61. for i := 0; i < maxRetry; i++ {
  62. select {
  63. case attemptCh <- i + 1:
  64. case <-ctx.Done():
  65. return
  66. }
  67. select {
  68. case <-time.After(exponentialBackoffWait(i)):
  69. case <-ctx.Done():
  70. return
  71. }
  72. }
  73. }()
  74. return attemptCh
  75. }
  76. // List of AWS S3 error codes which are retryable.
  77. var retryableS3Codes = map[string]struct{}{
  78. "RequestError": {},
  79. "RequestTimeout": {},
  80. "Throttling": {},
  81. "ThrottlingException": {},
  82. "RequestLimitExceeded": {},
  83. "RequestThrottled": {},
  84. "InternalError": {},
  85. "ExpiredToken": {},
  86. "ExpiredTokenException": {},
  87. "SlowDown": {},
  88. // Add more AWS S3 codes here.
  89. }
  90. // isS3CodeRetryable - is s3 error code retryable.
  91. func isS3CodeRetryable(s3Code string) (ok bool) {
  92. _, ok = retryableS3Codes[s3Code]
  93. return ok
  94. }
  95. // List of HTTP status codes which are retryable.
  96. var retryableHTTPStatusCodes = map[int]struct{}{
  97. 429: {}, // http.StatusTooManyRequests is not part of the Go 1.5 library, yet
  98. http.StatusInternalServerError: {},
  99. http.StatusBadGateway: {},
  100. http.StatusServiceUnavailable: {},
  101. http.StatusGatewayTimeout: {},
  102. // Add more HTTP status codes here.
  103. }
  104. // isHTTPStatusRetryable - is HTTP error code retryable.
  105. func isHTTPStatusRetryable(httpStatusCode int) (ok bool) {
  106. _, ok = retryableHTTPStatusCodes[httpStatusCode]
  107. return ok
  108. }