Skip to content
Merged
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
6 changes: 6 additions & 0 deletions .changelog/bugfix-fix-new-retries-gaps.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"type": "bugfix",
"category": "aws-cpp-sdk-core",
"contributor": "pulimsr",
"description": "Fix AWS_NEW_RETRIES_2026 gating: restore DefaultRetryStrategy fallback when the flag is off, read the flag case-insensitively in smithy clients, and let AWS_MAX_ATTEMPTS override the DynamoDB default when the flag is on"
}
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,7 @@ namespace client
ResponseHandlerFunc&& responseHandler,
EndpointUpdateCallback&& endpointCallback
) const;
bool m_enableNewRetries;
bool m_enableNewRetries = false;
};
} // namespace client
} // namespace smithy
67 changes: 30 additions & 37 deletions src/aws-cpp-sdk-core/source/client/ClientConfiguration.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -617,33 +617,47 @@ std::shared_ptr<RetryStrategy> InitRetryStrategy(int maxAttempts, Aws::String re
}
else
{
if (Aws::Utils::StringUtils::ToLower(Aws::Environment::GetEnv("AWS_NEW_RETRIES_2026").c_str()) != "true")
{
if (maxAttempts < 0)
{
retryStrategy = Aws::MakeShared<StandardRetryStrategy>(CLIENT_CONFIG_TAG);
}
else
{
retryStrategy = Aws::MakeShared<StandardRetryStrategy>(CLIENT_CONFIG_TAG, maxAttempts);
}
}
else
{
retryStrategy = Aws::MakeShared<DefaultRetryStrategy>(CLIENT_CONFIG_TAG);
}
retryStrategy = Aws::MakeShared<DefaultRetryStrategy>(CLIENT_CONFIG_TAG);
}

return retryStrategy;
}

static int ResolveConfiguredMaxAttempts()
{
Aws::String maxAttemptsString = Aws::Environment::GetEnv("AWS_MAX_ATTEMPTS");
if (maxAttemptsString.empty())
{
maxAttemptsString = Aws::Config::GetCachedConfigValue("max_attempts");
}
// In case users specify 0 explicitly to disable retry.
if (maxAttemptsString == "0")
{
return 0;
}
int maxAttempts = static_cast<int>(Aws::Utils::StringUtils::ConvertToInt32(maxAttemptsString.c_str()));
if (maxAttempts == 0)
{
AWS_LOGSTREAM_INFO(CLIENT_CONFIG_TAG, "Retry Strategy will use the default max attempts.");
return -1;
}
return maxAttempts;
}

std::shared_ptr<RetryStrategy> InitRetryStrategy(int maxAttempts, Aws::String retryMode, double transientBackoffBaseSec) {
if (Aws::Utils::StringUtils::ToLower(Aws::Environment::GetEnv("AWS_NEW_RETRIES_2026").c_str()) != "true")
{
// Gate is off: ignore service-specific tuning, use default behavior
return InitRetryStrategy(retryMode);
}

// maxAttempts is a service default. A max attempts value the customer configured wins.
const int configuredMaxAttempts = ResolveConfiguredMaxAttempts();
if (configuredMaxAttempts >= 0)
{
maxAttempts = configuredMaxAttempts;
}

// Only standard mode honors a service-specific backoff base; other modes are unaffected,
// so they go through the default-base path.
if (ResolveRetryMode(retryMode) != "standard")
Expand All @@ -657,28 +671,7 @@ std::shared_ptr<RetryStrategy> InitRetryStrategy(int maxAttempts, Aws::String re

std::shared_ptr<RetryStrategy> InitRetryStrategy(Aws::String retryMode)
{
int maxAttempts = 0;
Aws::String maxAttemptsString = Aws::Environment::GetEnv("AWS_MAX_ATTEMPTS");
if (maxAttemptsString.empty())
{
maxAttemptsString = Aws::Config::GetCachedConfigValue("max_attempts");
}
// In case users specify 0 explicitly to disable retry.
if (maxAttemptsString == "0")
{
maxAttempts = 0;
}
else
{
maxAttempts = static_cast<int>(Aws::Utils::StringUtils::ConvertToInt32(maxAttemptsString.c_str()));
if (maxAttempts == 0)
{
AWS_LOGSTREAM_INFO(CLIENT_CONFIG_TAG, "Retry Strategy will use the default max attempts.");
maxAttempts = -1;
}
}

return InitRetryStrategy(maxAttempts, retryMode);
return InitRetryStrategy(ResolveConfiguredMaxAttempts(), retryMode);
}

Aws::String ClientConfiguration::LoadConfigFromEnvOrProfile(const Aws::String& envKey,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ void AwsSmithyClientBase::baseCopyAssign(const AwsSmithyClientBase& other,
std::shared_ptr<Aws::Client::AWSErrorMarshaller> errorMarshaller) {
m_serviceName = other.m_serviceName;
m_serviceUserAgentName = other.m_serviceUserAgentName;
m_enableNewRetries = other.m_enableNewRetries;
m_httpClient = std::move(httpClient);
m_errorMarshaller = std::move(errorMarshaller);

Expand All @@ -129,6 +130,7 @@ void AwsSmithyClientBase::baseMoveAssign(AwsSmithyClientBase&& other) {
m_errorMarshaller = std::move(other.m_errorMarshaller);
m_interceptors = std::move(other.m_interceptors);
m_userAgentInterceptor = std::move(other.m_userAgentInterceptor);
m_enableNewRetries = other.m_enableNewRetries;
}

std::shared_ptr<Aws::Http::HttpRequest>
Expand Down Expand Up @@ -335,10 +337,7 @@ std::shared_ptr<AwsSmithyClientAsyncRequestContext> AwsSmithyClientBase::CreateR
}
pRequestCtx->m_requestInfo.attempt = 1;
pRequestCtx->m_attemptSkew = m_clientSkew->Load();
pRequestCtx->m_requestInfo.maxAttempts =
Aws::Environment::GetEnv("AWS_NEW_RETRIES_2026") == "true"
? m_clientConfig->retryStrategy->GetMaxAttempts()
: 0;
pRequestCtx->m_requestInfo.maxAttempts = m_enableNewRetries ? m_clientConfig->retryStrategy->GetMaxAttempts() : 0;
pRequestCtx->m_interceptorContext = Aws::MakeShared<InterceptorContext>(AWS_SMITHY_CLIENT_LOG, *request);
pRequestCtx->m_responseHandler = std::move(responseHandler);
pRequestCtx->m_authResolvedCallback = std::move(authCallback);
Expand Down
39 changes: 39 additions & 0 deletions tests/aws-cpp-sdk-core-tests/aws/client/RetryStrategyTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@
#include <aws/testing/AwsCppSdkGTestSuite.h>
#include <aws/testing/platform/PlatformTesting.h>
#include <aws/core/client/AWSError.h>
#include <aws/core/client/ClientConfiguration.h>
#include <aws/core/client/CoreErrors.h>
#include <aws/core/client/RetryStrategy.h>
#include <aws/core/config/ConfigAndCredentialsCacheManager.h>
#include <aws/core/http/URI.h>
#include <aws/core/http/HttpClientFactory.h>
#include <aws/core/http/standard/StandardHttpRequest.h>
Expand Down Expand Up @@ -317,3 +319,40 @@ TEST_F(NewRetriesStrategyTest, DynamoDBBackoffBase)
ASSERT_GE(delay, 0);
ASSERT_LE(delay, 1000);
}

class InitRetryStrategyTest : public Aws::Testing::AwsCppSdkGTestSuite
{
protected:
void SetUp() override { Aws::Config::ReloadCachedConfigFile(); }
void TearDown() override { Aws::Config::ReloadCachedConfigFile(); }
};

TEST_F(InitRetryStrategyTest, GateOffUnsetRetryModeUsesDefaultRetryStrategy)
{
Aws::Environment::EnvironmentRAII env{{{"AWS_NEW_RETRIES_2026", ""},
{"AWS_RETRY_MODE", ""},
{"AWS_MAX_ATTEMPTS", ""},
{"AWS_CONFIG_FILE", "/nonexistent/retry-strategy-test-config"}}};
Aws::Config::ReloadCachedConfigFile();

auto retryStrategy = InitRetryStrategy();
ASSERT_TRUE(retryStrategy);
ASSERT_STREQ("default", retryStrategy->GetStrategyName());
ASSERT_EQ(11, retryStrategy->GetMaxAttempts());
}

TEST_F(InitRetryStrategyTest, ConfiguredMaxAttemptsOverridesServiceDefault)
{
for (const auto& testCase : {std::make_pair("", 4L), std::make_pair("2", 2L), std::make_pair("0", 0L)})
{
Aws::Environment::EnvironmentRAII env{{{"AWS_NEW_RETRIES_2026", "true"},
{"AWS_RETRY_MODE", ""},
{"AWS_MAX_ATTEMPTS", testCase.first},
{"AWS_CONFIG_FILE", "/nonexistent/retry-strategy-test-config"}}};
Aws::Config::ReloadCachedConfigFile();

auto retryStrategy = InitRetryStrategy(4, "", 0.025);
ASSERT_TRUE(retryStrategy);
ASSERT_EQ(testCase.second, retryStrategy->GetMaxAttempts()) << "AWS_MAX_ATTEMPTS=" << testCase.first;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include <aws/core/client/ClientConfiguration.h>
#include <aws/core/client/CoreErrors.h>
#include <aws/core/client/DefaultRetryStrategy.h>
#include <aws/core/client/RetryStrategy.h>
#include <aws/core/endpoint/AWSEndpoint.h>
#include <aws/core/endpoint/EndpointProviderBase.h>
#include <aws/core/http/HttpClientFactory.h>
Expand Down Expand Up @@ -300,4 +301,18 @@ namespace
ASSERT_LT(elapsed, std::chrono::milliseconds(LONG_POLL_DELAY_MS));
#endif
}

TEST_F(AsyncSmithyClientTest, NewRetriesFlagIsCaseInsensitive)
{
Aws::Environment::EnvironmentRAII env{{{"AWS_NEW_RETRIES_2026", "TRUE"}}};
client = MakeClient(Aws::MakeShared<StandardRetryStrategy>(ALLOCATION_TAG, 3));
mockHttpClient->AddResponseToReturn(MakeSuccessResponse());

auto outcome = RunAndCapture();

ASSERT_TRUE(outcome.IsSuccess());
const auto& requests = mockHttpClient->GetAllRequestsMade();
ASSERT_EQ(1u, requests.size());
ASSERT_NE(Aws::String::npos, requests[0].GetHeaders()[Aws::Http::SDK_REQUEST_HEADER].find("max=3"));
}
}
Loading