/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
using Amazon.Util;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Threading;
namespace Amazon.Runtime.Internal
{
///
/// The default implementation of the adaptive retry policy.
///
public partial class AdaptiveRetryPolicy : StandardRetryPolicy
{
protected TokenBucket TokenBucket { get; set; } = new TokenBucket();
///
/// Constructor for AdaptiveRetryPolicy.
///
/// The maximum number of retries before throwing
/// back a exception. This does not count the initial request.
public AdaptiveRetryPolicy(int maxRetries) : base(maxRetries)
{
}
///
/// Constructor for AdaptiveRetryPolicy.
///
/// The Client config object. This is used to
/// retrieve the maximum number of retries before throwing
/// back a exception(This does not count the initial request) and
/// the service URL for the request.
public AdaptiveRetryPolicy(IClientConfig config) : base(config)
{
}
///
/// OnRetry is called when a retry request is initiated to determine if the request will be retried.
///
/// The execution context which contains both the
/// requests and response context.
/// true to bypass any attempt to acquire capacity on a retry
/// true if the error that will be retried is a throtting error
/// True if retry throttling is disabled or retry throttling is enabled and capacity can be obtained.
public override bool OnRetry(IExecutionContext executionContext, bool bypassAcquireCapacity, bool isThrottlingError)
{
TokenBucket.UpdateClientSendingRate(isThrottlingError);
return base.OnRetry(executionContext, bypassAcquireCapacity, isThrottlingError);
}
///
/// This method uses a token bucket to enforce the maximum sending rate.
///
/// The execution context which contains both the
/// requests and response context.
/// If the prior request failed, this exception is expected to be
/// the exception that occurred during the prior request failure.
public override void ObtainSendToken(IExecutionContext executionContext, Exception exception)
{
if (!TokenBucket.TryAcquireToken(1, executionContext.RequestContext.ClientConfig.FastFailRequests))
{
var whyFail = exception == null ? "The initial request cannot be attempted because capacity could not be obtained"
: "While attempting to retry a request error capacity could not be obtained";
if (executionContext.RequestContext.ClientConfig.FastFailRequests)
{
throw new AmazonClientException($"{whyFail}. The client is configured to fail fast and there is insufficent capacity to attempt the request.", exception);
}
//Else we were unable to obtain capacity after looping.
throw new AmazonClientException($"{whyFail}. There is insufficent capacity to attempt the request after attempting to obtain capacity multiple times.", exception);
}
}
///
/// Virtual method that gets called on a success Response. If its a retry success response, the entire
/// retry acquired capacity is released(default is 5). If its just a success response a lesser value capacity
/// is released(default is 1).
///
/// Request context containing the state of the request.
public override void NotifySuccess(IExecutionContext executionContext)
{
TokenBucket.UpdateClientSendingRate(false);
base.NotifySuccess(executionContext);
}
}
}