github.com/aavshr/aws-sdk-go@v1.41.3/aws/client/default_retryer.go (about)

     1  package client
     2  
     3  import (
     4  	"math"
     5  	"strconv"
     6  	"time"
     7  
     8  	"github.com/aavshr/aws-sdk-go/aws/request"
     9  	"github.com/aavshr/aws-sdk-go/internal/sdkrand"
    10  )
    11  
    12  // DefaultRetryer implements basic retry logic using exponential backoff for
    13  // most services. If you want to implement custom retry logic, you can implement the
    14  // request.Retryer interface.
    15  //
    16  type DefaultRetryer struct {
    17  	// Num max Retries is the number of max retries that will be performed.
    18  	// By default, this is zero.
    19  	NumMaxRetries int
    20  
    21  	// MinRetryDelay is the minimum retry delay after which retry will be performed.
    22  	// If not set, the value is 0ns.
    23  	MinRetryDelay time.Duration
    24  
    25  	// MinThrottleRetryDelay is the minimum retry delay when throttled.
    26  	// If not set, the value is 0ns.
    27  	MinThrottleDelay time.Duration
    28  
    29  	// MaxRetryDelay is the maximum retry delay before which retry must be performed.
    30  	// If not set, the value is 0ns.
    31  	MaxRetryDelay time.Duration
    32  
    33  	// MaxThrottleDelay is the maximum retry delay when throttled.
    34  	// If not set, the value is 0ns.
    35  	MaxThrottleDelay time.Duration
    36  }
    37  
    38  const (
    39  	// DefaultRetryerMaxNumRetries sets maximum number of retries
    40  	DefaultRetryerMaxNumRetries = 3
    41  
    42  	// DefaultRetryerMinRetryDelay sets minimum retry delay
    43  	DefaultRetryerMinRetryDelay = 30 * time.Millisecond
    44  
    45  	// DefaultRetryerMinThrottleDelay sets minimum delay when throttled
    46  	DefaultRetryerMinThrottleDelay = 500 * time.Millisecond
    47  
    48  	// DefaultRetryerMaxRetryDelay sets maximum retry delay
    49  	DefaultRetryerMaxRetryDelay = 300 * time.Second
    50  
    51  	// DefaultRetryerMaxThrottleDelay sets maximum delay when throttled
    52  	DefaultRetryerMaxThrottleDelay = 300 * time.Second
    53  )
    54  
    55  // MaxRetries returns the number of maximum returns the service will use to make
    56  // an individual API request.
    57  func (d DefaultRetryer) MaxRetries() int {
    58  	return d.NumMaxRetries
    59  }
    60  
    61  // setRetryerDefaults sets the default values of the retryer if not set
    62  func (d *DefaultRetryer) setRetryerDefaults() {
    63  	if d.MinRetryDelay == 0 {
    64  		d.MinRetryDelay = DefaultRetryerMinRetryDelay
    65  	}
    66  	if d.MaxRetryDelay == 0 {
    67  		d.MaxRetryDelay = DefaultRetryerMaxRetryDelay
    68  	}
    69  	if d.MinThrottleDelay == 0 {
    70  		d.MinThrottleDelay = DefaultRetryerMinThrottleDelay
    71  	}
    72  	if d.MaxThrottleDelay == 0 {
    73  		d.MaxThrottleDelay = DefaultRetryerMaxThrottleDelay
    74  	}
    75  }
    76  
    77  // RetryRules returns the delay duration before retrying this request again
    78  func (d DefaultRetryer) RetryRules(r *request.Request) time.Duration {
    79  
    80  	// if number of max retries is zero, no retries will be performed.
    81  	if d.NumMaxRetries == 0 {
    82  		return 0
    83  	}
    84  
    85  	// Sets default value for retryer members
    86  	d.setRetryerDefaults()
    87  
    88  	// minDelay is the minimum retryer delay
    89  	minDelay := d.MinRetryDelay
    90  
    91  	var initialDelay time.Duration
    92  
    93  	isThrottle := r.IsErrorThrottle()
    94  	if isThrottle {
    95  		if delay, ok := getRetryAfterDelay(r); ok {
    96  			initialDelay = delay
    97  		}
    98  		minDelay = d.MinThrottleDelay
    99  	}
   100  
   101  	retryCount := r.RetryCount
   102  
   103  	// maxDelay the maximum retryer delay
   104  	maxDelay := d.MaxRetryDelay
   105  
   106  	if isThrottle {
   107  		maxDelay = d.MaxThrottleDelay
   108  	}
   109  
   110  	var delay time.Duration
   111  
   112  	// Logic to cap the retry count based on the minDelay provided
   113  	actualRetryCount := int(math.Log2(float64(minDelay))) + 1
   114  	if actualRetryCount < 63-retryCount {
   115  		delay = time.Duration(1<<uint64(retryCount)) * getJitterDelay(minDelay)
   116  		if delay > maxDelay {
   117  			delay = getJitterDelay(maxDelay / 2)
   118  		}
   119  	} else {
   120  		delay = getJitterDelay(maxDelay / 2)
   121  	}
   122  	return delay + initialDelay
   123  }
   124  
   125  // getJitterDelay returns a jittered delay for retry
   126  func getJitterDelay(duration time.Duration) time.Duration {
   127  	return time.Duration(sdkrand.SeededRand.Int63n(int64(duration)) + int64(duration))
   128  }
   129  
   130  // ShouldRetry returns true if the request should be retried.
   131  func (d DefaultRetryer) ShouldRetry(r *request.Request) bool {
   132  
   133  	// ShouldRetry returns false if number of max retries is 0.
   134  	if d.NumMaxRetries == 0 {
   135  		return false
   136  	}
   137  
   138  	// If one of the other handlers already set the retry state
   139  	// we don't want to override it based on the service's state
   140  	if r.Retryable != nil {
   141  		return *r.Retryable
   142  	}
   143  	return r.IsErrorRetryable() || r.IsErrorThrottle()
   144  }
   145  
   146  // This will look in the Retry-After header, RFC 7231, for how long
   147  // it will wait before attempting another request
   148  func getRetryAfterDelay(r *request.Request) (time.Duration, bool) {
   149  	if !canUseRetryAfterHeader(r) {
   150  		return 0, false
   151  	}
   152  
   153  	delayStr := r.HTTPResponse.Header.Get("Retry-After")
   154  	if len(delayStr) == 0 {
   155  		return 0, false
   156  	}
   157  
   158  	delay, err := strconv.Atoi(delayStr)
   159  	if err != nil {
   160  		return 0, false
   161  	}
   162  
   163  	return time.Duration(delay) * time.Second, true
   164  }
   165  
   166  // Will look at the status code to see if the retry header pertains to
   167  // the status code.
   168  func canUseRetryAfterHeader(r *request.Request) bool {
   169  	switch r.HTTPResponse.StatusCode {
   170  	case 429:
   171  	case 503:
   172  	default:
   173  		return false
   174  	}
   175  
   176  	return true
   177  }