77#include < aws/core/client/CoreErrors.h>
88#include < aws/core/client/RetryStrategy.h>
99#include < aws/core/utils/Outcome.h>
10+ #include < aws/core/utils/StringUtils.h>
1011#include < aws/core/utils/local/Random.h>
1112
1213using namespace Aws ::Utils::Threading;
@@ -19,11 +20,98 @@ namespace Aws
1920 static const int RETRY_COST = 5 ;
2021 static const int TIMEOUT_RETRY_COST = 10 ;
2122
23+ // ---- DefaultRetryQuotaContainer ----
24+
25+ DefaultRetryQuotaContainer::DefaultRetryQuotaContainer ()
26+ : m_retryQuota(INITIAL_RETRY_TOKENS ),
27+ m_retryCost (RETRY_COST ),
28+ m_throttlingRetryCost(TIMEOUT_RETRY_COST ),
29+ m_classification(RetryCostClassification::REQUEST_TIMEOUT_BASED )
30+ {}
31+
32+ DefaultRetryQuotaContainer::DefaultRetryQuotaContainer (int retryCost, int throttlingRetryCost, RetryCostClassification classification)
33+ : m_retryQuota(INITIAL_RETRY_TOKENS ),
34+ m_retryCost(retryCost),
35+ m_throttlingRetryCost(throttlingRetryCost),
36+ m_classification(classification)
37+ {}
38+
39+ bool DefaultRetryQuotaContainer::AcquireRetryQuota (int capacityAmount)
40+ {
41+ WriterLockGuard guard (m_retryQuotaLock);
42+
43+ if (capacityAmount > m_retryQuota)
44+ {
45+ return false ;
46+ }
47+ m_retryQuota -= capacityAmount;
48+ return true ;
49+ }
50+
51+ bool DefaultRetryQuotaContainer::AcquireRetryQuota (const AWSError<CoreErrors>& error)
52+ {
53+ int capacityAmount;
54+ if (m_classification == RetryCostClassification::THROTTLE_BASED )
55+ {
56+ capacityAmount = error.ShouldThrottle () ? m_throttlingRetryCost : m_retryCost;
57+ }
58+ else
59+ {
60+ capacityAmount = error.GetErrorType () == CoreErrors::REQUEST_TIMEOUT ? TIMEOUT_RETRY_COST : RETRY_COST ;
61+ }
62+ return AcquireRetryQuota (capacityAmount);
63+ }
64+
65+ void DefaultRetryQuotaContainer::ReleaseRetryQuota (int capacityAmount)
66+ {
67+ WriterLockGuard guard (m_retryQuotaLock);
68+ m_retryQuota = (std::min)(m_retryQuota + capacityAmount, INITIAL_RETRY_TOKENS );
69+ }
70+
71+ void DefaultRetryQuotaContainer::ReleaseRetryQuota (const AWSError<CoreErrors>& error)
72+ {
73+ int capacityAmount;
74+ if (m_classification == RetryCostClassification::THROTTLE_BASED )
75+ {
76+ capacityAmount = error.ShouldThrottle () ? m_throttlingRetryCost : m_retryCost;
77+ }
78+ else
79+ {
80+ capacityAmount = error.GetErrorType () == CoreErrors::REQUEST_TIMEOUT ? TIMEOUT_RETRY_COST : RETRY_COST ;
81+ }
82+ ReleaseRetryQuota (capacityAmount);
83+ }
84+
85+ // ---- StandardRetryStrategy pimpl ----
86+
87+ struct StandardRetryStrategy ::RetryImpl
88+ {
89+ bool newRetriesEnabled = false ;
90+ double transientBackoffBaseSec = 0.05 ;
91+ };
92+
2293 StandardRetryStrategy::StandardRetryStrategy (long maxAttempts)
23- : m_retryQuotaContainer(Aws::MakeShared<DefaultRetryQuotaContainer>(" StandardRetryStrategy" )), m_maxAttempts(maxAttempts) {}
94+ : m_retryQuotaContainer(Aws::MakeShared<DefaultRetryQuotaContainer>(" StandardRetryStrategy" )),
95+ m_maxAttempts(maxAttempts),
96+ m_impl(Aws::MakeUnique<RetryImpl>(" StandardRetryStrategy" ))
97+ {}
2498
2599 StandardRetryStrategy::StandardRetryStrategy (std::shared_ptr<RetryQuotaContainer> retryQuotaContainer, long maxAttempts)
26- : m_retryQuotaContainer(retryQuotaContainer), m_maxAttempts(maxAttempts) {}
100+ : m_retryQuotaContainer(retryQuotaContainer),
101+ m_maxAttempts(maxAttempts),
102+ m_impl(Aws::MakeUnique<RetryImpl>(" StandardRetryStrategy" ))
103+ {}
104+
105+ StandardRetryStrategy::StandardRetryStrategy (std::shared_ptr<RetryQuotaContainer> retryQuotaContainer, long maxAttempts, double transientBackoffBaseSec)
106+ : m_retryQuotaContainer(retryQuotaContainer),
107+ m_maxAttempts(maxAttempts),
108+ m_impl(Aws::MakeUnique<RetryImpl>(" StandardRetryStrategy" ))
109+ {
110+ m_impl->newRetriesEnabled = true ;
111+ m_impl->transientBackoffBaseSec = transientBackoffBaseSec;
112+ }
113+
114+ StandardRetryStrategy::~StandardRetryStrategy () = default ;
27115
28116 void StandardRetryStrategy::RequestBookkeeping (const HttpResponseOutcome& httpResponseOutcome)
29117 {
@@ -54,45 +142,30 @@ namespace Aws
54142
55143 long StandardRetryStrategy::CalculateDelayBeforeNextRetry (const AWSError<CoreErrors>& error, long attemptedRetries) const
56144 {
57- AWS_UNREFERENCED_PARAM (error);
58- // Maximum left shift factor is capped by ceil(log2(max_delay)), to avoid wrap-around and overflow into negative values:
59- return std::min (static_cast <int >(Aws::Utils::GetRandomValue () % 1000 ) * (1 << std::min (attemptedRetries, 15L )), 20000 );
60- }
145+ if (!m_impl->newRetriesEnabled )
146+ {
147+ AWS_UNREFERENCED_PARAM (error);
148+ return (std::min)(static_cast <int >(Aws::Utils::GetRandomValue () % 1000 ) * (1 << (std::min)(attemptedRetries, 15L )), 20000 );
149+ }
61150
62- DefaultRetryQuotaContainer::DefaultRetryQuotaContainer () : m_retryQuota(INITIAL_RETRY_TOKENS )
63- {}
151+ double x = error.ShouldThrottle () ? 1.0 : m_impl->transientBackoffBaseSec ;
152+ double exponentialPart = x * static_cast <double >(1L << (std::min)(attemptedRetries, 30L ));
153+ double cappedPart = (std::min)(exponentialPart, 20.0 );
64154
65- bool DefaultRetryQuotaContainer::AcquireRetryQuota (int capacityAmount)
66- {
67- WriterLockGuard guard (m_retryQuotaLock);
155+ double b = static_cast <double >(Aws::Utils::GetRandomValue () % 10000 ) / 10000.0 ;
156+ double t_i = b * cappedPart;
68157
69- if (capacityAmount > m_retryQuota)
70- {
71- return false ;
72- }
73- else
158+ const auto & headers = error.GetResponseHeaders ();
159+ auto it = headers.find (" x-amz-retry-after" );
160+ if (it != headers.end ())
74161 {
75- m_retryQuota -= capacityAmount;
76- return true ;
162+ double headerSec = static_cast <double >(Aws::Utils::StringUtils::ConvertToInt64 (it->second .c_str ())) / 1000.0 ;
163+ double clamped = (std::max)(t_i, (std::min)(headerSec, 5.0 + t_i));
164+ return static_cast <long >(clamped * 1000.0 );
77165 }
78- }
79166
80- bool DefaultRetryQuotaContainer::AcquireRetryQuota (const AWSError<CoreErrors>& error)
81- {
82- int capacityAmount = error.GetErrorType () == CoreErrors::REQUEST_TIMEOUT ? TIMEOUT_RETRY_COST : RETRY_COST ;
83- return AcquireRetryQuota (capacityAmount);
167+ return static_cast <long >(t_i * 1000.0 );
84168 }
85169
86- void DefaultRetryQuotaContainer::ReleaseRetryQuota (int capacityAmount)
87- {
88- WriterLockGuard guard (m_retryQuotaLock);
89- m_retryQuota = (std::min)(m_retryQuota + capacityAmount, INITIAL_RETRY_TOKENS );
90- }
91-
92- void DefaultRetryQuotaContainer::ReleaseRetryQuota (const AWSError<CoreErrors>& error)
93- {
94- int capacityAmount = error.GetErrorType () == CoreErrors::REQUEST_TIMEOUT ? TIMEOUT_RETRY_COST : RETRY_COST ;
95- ReleaseRetryQuota (capacityAmount);
96- }
97170 }
98171}
0 commit comments