Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions src/aws-cpp-sdk-core/include/aws/core/client/RetryStrategy.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#pragma once

#include <aws/core/Core_EXPORTS.h>
#include <aws/core/utils/memory/AWSMemory.h>
#include <aws/core/utils/threading/ReaderWriterLock.h>
#include <memory>

Expand Down Expand Up @@ -123,6 +124,7 @@ namespace Aws
public:
StandardRetryStrategy(long maxAttempts = 3);
StandardRetryStrategy(std::shared_ptr<RetryQuotaContainer> retryQuotaContainer, long maxAttempts = 3);
virtual ~StandardRetryStrategy();

virtual void RequestBookkeeping(const HttpResponseOutcome& httpResponseOutcome) override;
virtual void RequestBookkeeping(const HttpResponseOutcome& httpResponseOutcome, const AWSError<CoreErrors>& lastError) override;
Expand All @@ -135,9 +137,14 @@ namespace Aws

const char* GetStrategyName() const override { return "standard";}

struct RetryImpl;

protected:
std::shared_ptr<RetryQuotaContainer> m_retryQuotaContainer;
long m_maxAttempts;

private:
Aws::UniquePtr<RetryImpl> m_impl;
};
} // namespace Client
} // namespace Aws
71 changes: 71 additions & 0 deletions src/aws-cpp-sdk-core/include/aws/core/internal/RetryStrategyImpl.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/**
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0.
*/

#pragma once

#include <aws/core/Core_EXPORTS.h>
#include <aws/core/client/AWSError.h>
#include <aws/core/client/CoreErrors.h>
#include <aws/core/client/RetryStrategy.h>
#include <aws/core/utils/threading/ReaderWriterLock.h>

namespace Aws
{
namespace Client
{
static const int THROTTLE_BASED_RETRY_COST = 14;
static const int THROTTLE_BASED_THROTTLING_COST = 5;
static const int THROTTLE_BASED_INITIAL_TOKENS = 500;

class AWS_CORE_LOCAL ThrottleBasedRetryQuotaContainer : public RetryQuotaContainer
{
public:
ThrottleBasedRetryQuotaContainer(int retryCost = THROTTLE_BASED_RETRY_COST, int throttlingRetryCost = THROTTLE_BASED_THROTTLING_COST)
: m_retryQuota(THROTTLE_BASED_INITIAL_TOKENS), m_retryCost(retryCost), m_throttlingRetryCost(throttlingRetryCost) {}

virtual ~ThrottleBasedRetryQuotaContainer() = default;

bool AcquireRetryQuota(int capacityAmount) override
{
Aws::Utils::Threading::WriterLockGuard guard(m_retryQuotaLock);
if (capacityAmount > m_retryQuota)
{
return false;
}
else
{
m_retryQuota -= capacityAmount;
return true;
}
}

bool AcquireRetryQuota(const AWSError<CoreErrors>& error) override
{
int capacityAmount = error.ShouldThrottle() ? m_throttlingRetryCost : m_retryCost;
return AcquireRetryQuota(capacityAmount);
}

void ReleaseRetryQuota(int capacityAmount) override
{
Aws::Utils::Threading::WriterLockGuard guard(m_retryQuotaLock);
m_retryQuota = (std::min)(m_retryQuota + capacityAmount, THROTTLE_BASED_INITIAL_TOKENS);
}

void ReleaseRetryQuota(const AWSError<CoreErrors>& error) override
{
int capacityAmount = error.ShouldThrottle() ? m_throttlingRetryCost : m_retryCost;
ReleaseRetryQuota(capacityAmount);
}

int GetRetryQuota() const override { return m_retryQuota; }

private:
mutable Aws::Utils::Threading::ReaderWriterLock m_retryQuotaLock;
int m_retryQuota;
int m_retryCost;
int m_throttlingRetryCost;
};
} // namespace Client
} // namespace Aws
4 changes: 4 additions & 0 deletions src/aws-cpp-sdk-core/source/client/ClientConfiguration.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -552,6 +552,10 @@ std::shared_ptr<RetryStrategy> InitRetryStrategy(int maxAttempts, Aws::String re
{
retryMode = Aws::Config::GetCachedConfigValue("retry_mode");
}
if (Aws::Environment::GetEnv("AWS_NEW_RETRIES_2026") == "true" && retryMode.empty())
{
retryMode = "standard";
}

std::shared_ptr<RetryStrategy> retryStrategy;
if (retryMode == "standard")
Expand Down
75 changes: 70 additions & 5 deletions src/aws-cpp-sdk-core/source/client/RetryStrategy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@
#include <aws/core/client/AWSError.h>
#include <aws/core/client/CoreErrors.h>
#include <aws/core/client/RetryStrategy.h>
#include <aws/core/internal/RetryStrategyImpl.h>
#include <aws/core/platform/Environment.h>
#include <aws/core/utils/Outcome.h>
#include <aws/core/utils/StringUtils.h>
#include <aws/core/utils/local/Random.h>

using namespace Aws::Utils::Threading;
Expand All @@ -19,11 +22,75 @@ namespace Aws
static const int RETRY_COST = 5;
static const int TIMEOUT_RETRY_COST = 10;

struct StandardRetryStrategy::RetryImpl
{
virtual ~RetryImpl() = default;
virtual long CalculateDelay(const AWSError<CoreErrors>& error, long attemptedRetries) const = 0;
};

namespace {
struct LegacyRetryImpl : StandardRetryStrategy::RetryImpl
{
long CalculateDelay(const AWSError<CoreErrors>& error, long attemptedRetries) const override
{
AWS_UNREFERENCED_PARAM(error);
// Maximum left shift factor is capped by ceil(log2(max_delay)), to avoid wrap-around and overflow into negative values:
return std::min(static_cast<int>(Aws::Utils::GetRandomValue() % 1000) * (1 << std::min(attemptedRetries, 15L)), 20000);
}
};

struct NewRetriesImpl : StandardRetryStrategy::RetryImpl
{
long CalculateDelay(const AWSError<CoreErrors>& error, long attemptedRetries) const override
{
double x = error.ShouldThrottle() ? 1.0 : 0.05;
double exponentialPart = x * static_cast<double>(1L << (std::min)(attemptedRetries, 30L));
double cappedPart = (std::min)(exponentialPart, 20.0);

double b = static_cast<double>(Aws::Utils::GetRandomValue() % 10000) / 10000.0;
double t_i = b * cappedPart;

const auto& headers = error.GetResponseHeaders();
auto it = headers.find("x-amz-retry-after");
if (it != headers.end())
{
double headerSec = static_cast<double>(Aws::Utils::StringUtils::ConvertToInt64(it->second.c_str())) / 1000.0;
double clamped = (std::max)(t_i, (std::min)(headerSec, 5.0 + t_i));
return static_cast<long>(clamped * 1000.0);
}

return static_cast<long>(t_i * 1000.0);
}
};
} // anonymous namespace

static Aws::UniquePtr<StandardRetryStrategy::RetryImpl> CreateRetryImpl()
{
if (Aws::Environment::GetEnv("AWS_NEW_RETRIES_2026") == "true")
{
return Aws::MakeUnique<NewRetriesImpl>("StandardRetryStrategy");
}
return Aws::MakeUnique<LegacyRetryImpl>("StandardRetryStrategy");
}

static std::shared_ptr<RetryQuotaContainer> CreateQuotaContainer()
{
if (Aws::Environment::GetEnv("AWS_NEW_RETRIES_2026") == "true")
{
return Aws::MakeShared<ThrottleBasedRetryQuotaContainer>("StandardRetryStrategy");
}
return Aws::MakeShared<DefaultRetryQuotaContainer>("StandardRetryStrategy");
}

StandardRetryStrategy::StandardRetryStrategy(long maxAttempts)
: m_retryQuotaContainer(Aws::MakeShared<DefaultRetryQuotaContainer>("StandardRetryStrategy")), m_maxAttempts(maxAttempts) {}
: m_retryQuotaContainer(CreateQuotaContainer()), m_maxAttempts(maxAttempts),
m_impl(CreateRetryImpl()) {}

StandardRetryStrategy::StandardRetryStrategy(std::shared_ptr<RetryQuotaContainer> retryQuotaContainer, long maxAttempts)
: m_retryQuotaContainer(retryQuotaContainer), m_maxAttempts(maxAttempts) {}
: m_retryQuotaContainer(retryQuotaContainer), m_maxAttempts(maxAttempts),
m_impl(CreateRetryImpl()) {}

StandardRetryStrategy::~StandardRetryStrategy() = default;

void StandardRetryStrategy::RequestBookkeeping(const HttpResponseOutcome& httpResponseOutcome)
{
Expand Down Expand Up @@ -54,9 +121,7 @@ namespace Aws

long StandardRetryStrategy::CalculateDelayBeforeNextRetry(const AWSError<CoreErrors>& error, long attemptedRetries) const
{
AWS_UNREFERENCED_PARAM(error);
// Maximum left shift factor is capped by ceil(log2(max_delay)), to avoid wrap-around and overflow into negative values:
return std::min(static_cast<int>(Aws::Utils::GetRandomValue() % 1000) * (1 << std::min(attemptedRetries, 15L)), 20000);
return m_impl->CalculateDelay(error, attemptedRetries);
}

DefaultRetryQuotaContainer::DefaultRetryQuotaContainer() : m_retryQuota(INITIAL_RETRY_TOKENS)
Expand Down
Loading
Loading