Example #1
0
// ECSRetryHandler defines how to retry ECS service calls. It behaves like the default retry handler, except for the SubmitStateChange operations where it has a massive upper limit on retry counts
func ECSRetryHandler(r *aws.Request) {
	if r.Operation == nil || (r.Operation.Name != opSubmitContainerStateChange && r.Operation.Name != opSubmitTaskStateChange) {
		aws.AfterRetryHandler(r)
		return
	}
	// else this is a Submit*StateChange operation
	// For these operations, fake the retry count for the sake of the WillRetry check.
	// Do this by temporarily setting it to 0 before calling that check.
	// We still keep the value around for sleep calculations
	// See https://github.com/aws/aws-sdk-go/blob/b2d953f489cf94029392157225e893d7b69cd447/aws/handler_functions.go#L107
	// for this code's inspiration
	realRetryCount := r.RetryCount
	if r.RetryCount < maxSubmitRetryCount {
		r.RetryCount = 0
	}

	r.Retryable.Set(r.Service.ShouldRetry(r))
	if r.WillRetry() {
		r.RetryCount = realRetryCount
		if r.RetryCount > 20 {
			// Hardcoded max for calling RetryRules here because it *will* overflow if you let it and result in sleeping negative time
			r.RetryDelay = maxSubmitRetryDelay
		} else {
			r.RetryDelay = durationMin(maxSubmitRetryDelay, r.Service.RetryRules(r))
		}
		// AddJitter is purely additive, so subtracting half the amount of jitter
		// makes it average out to RetryDelay
		ttime.Sleep(utils.AddJitter(r.RetryDelay-submitRetryDelayJitter/2, submitRetryDelayJitter))

		if r.Error != nil {
			if err, ok := r.Error.(awserr.Error); ok {
				if isCodeExpiredCreds(err.Code()) {
					r.Config.Credentials.Expire()
				}
			}
		}

		r.RetryCount++
		r.Error = nil
	}
}
// newSubmitStateChangeClient returns a client intended to be used for
// Submit*StateChange APIs which has the behavior of retrying the call on
// retriable errors for an extended period of time (roughly 24 hours).
func newSubmitStateChangeClient(awsConfig *aws.Config) *ecs.ECS {
	sscConfig := awsConfig.Copy()
	sscConfig.MaxRetries = submitStateChangeMaxDelayRetries
	client := ecs.New(&sscConfig)
	client.Handlers.AfterRetry.Clear()
	client.Handlers.AfterRetry.PushBack(
		extendedRetryMaxDelayHandlerFactory(submitStateChangeExtraRetries))
	client.DefaultMaxRetries = submitStateChangeMaxDelayRetries
	return client
}

var awsAfterRetryHandler = func(r *aws.Request) {
	aws.AfterRetryHandler(r)
}

// ExtendedRetryMaxDelayHandlerFactory returns a function which can be used as an AfterRetryHandler
// in the AWS Go SDK.  This AfterRetryHandler can be used to have a large number of retries where
// the initial delay between backoff grows exponentially (2^n * 30ms; defined in service.retryRules)
// and extra retries are performed at the maximum delay of the initial exponential growth.
func extendedRetryMaxDelayHandlerFactory(maxExtendedRetries uint) func(r *aws.Request) {
	return func(r *aws.Request) {
		realRetryCount := r.RetryCount
		maxDelayRetries := r.MaxRetries()
		if r.RetryCount >= (maxDelayRetries + maxExtendedRetries) {
			return
		} else if r.RetryCount >= maxDelayRetries {
			r.RetryCount = maxDelayRetries - 1
		}