77use Aws \Exception \AwsException ;
88use Aws \HandlerList ;
99use Aws \Middleware ;
10+ use Aws \Retry \Configuration as RetryConfiguration ;
11+ use Aws \Retry \ConfigurationInterface as RetryConfigurationInterface ;
12+ use Aws \Retry \ConfigurationProvider as RetryConfigurationProvider ;
13+ use Aws \Retry \Standard \OptIn as NewRetriesOptIn ;
14+ use Aws \Retry \Standard \RetryMiddleware as StandardRetryMiddleware ;
1015use Aws \RetryMiddleware ;
1116use Aws \RetryMiddlewareV2 ;
17+ use GuzzleHttp \Promise \Create ;
1218
1319/**
1420 * This client is used to interact with the **Amazon DynamoDB** service.
130136 */
131137class DynamoDbClient extends AwsClient
132138{
139+ /** @internal Default attempts for the AWS_NEW_RETRIES_2026 path. */
140+ private const DYNAMODB_MAX_ATTEMPTS = 4 ;
141+ /** @internal Base backoff in ms for the AWS_NEW_RETRIES_2026 path. */
142+ private const DEFAULT_BASE_DELAY_MS = 25 ;
143+ /**
144+ * @internal Legacy-mode fallback when an array config does not specify
145+ * max_attempts. Only consulted on the AWS_NEW_RETRIES_2026 path.
146+ */
147+ public const DEFAULT_LEGACY_MAX_ATTEMPTS = 10 ;
148+
133149 public static function getArguments ()
134150 {
135151 $ args = parent ::getArguments ();
136- $ args ['retries ' ]['default ' ] = 10 ;
152+ $ args ['retries ' ]['default ' ] = NewRetriesOptIn::isEnabled ()
153+ ? [__CLASS__ , '_defaultRetries ' ]
154+ : self ::DEFAULT_LEGACY_MAX_ATTEMPTS ;
137155 $ args ['retries ' ]['fn ' ] = [__CLASS__ , '_applyRetryConfig ' ];
138156 $ args ['api_provider ' ]['fn ' ] = [__CLASS__ , '_applyApiProvider ' ];
139157
140158 return $ args ;
141159 }
142160
161+ /**
162+ * @internal Default retry-config provider for the AWS_NEW_RETRIES_2026
163+ * path. Falls through to env/INI before applying the DynamoDB
164+ * default of {@see self::DYNAMODB_MAX_ATTEMPTS} attempts in
165+ * the specs standard mode.
166+ */
167+ public static function _defaultRetries ()
168+ {
169+ return RetryConfigurationProvider::chain (
170+ RetryConfigurationProvider::env (),
171+ RetryConfigurationProvider::ini (),
172+ function () {
173+ return Create::promiseFor (
174+ new RetryConfiguration (
175+ RetryConfigurationProvider::getDefaultMode (),
176+ self ::DYNAMODB_MAX_ATTEMPTS
177+ )
178+ );
179+ }
180+ );
181+ }
182+
143183 /**
144184 * Convenience method for instantiating and registering the DynamoDB
145185 * Session handler with this DynamoDB client object.
@@ -159,40 +199,99 @@ public function registerSessionHandler(array $config = [])
159199 /** @internal */
160200 public static function _applyRetryConfig ($ value , array &$ args , HandlerList $ list )
161201 {
162- if ($ value ) {
163- $ config = \Aws \Retry \ConfigurationProvider::unwrap ($ value );
164-
165- if ($ config ->getMode () === 'legacy ' ) {
166- $ list ->appendSign (
167- Middleware::retry (
168- RetryMiddleware::createDefaultDecider (
169- $ config ->getMaxAttempts () - 1 ,
170- ['error_codes ' => ['TransactionInProgressException ' ]]
171- ),
172- function ($ retries ) {
173- return $ retries
174- ? RetryMiddleware::exponentialDelay ($ retries ) / 2
175- : 0 ;
176- },
177- isset ($ args ['stats ' ]['retries ' ])
178- ? (bool )$ args ['stats ' ]['retries ' ]
179- : false
180- ),
181- 'retry '
182- );
183- } else {
184- $ list ->appendSign (
185- RetryMiddlewareV2::wrap (
186- $ config ,
187- [
188- 'collect_stats ' => $ args ['stats ' ]['retries ' ],
189- 'transient_error_codes ' => ['TransactionInProgressException ' ]
190- ]
191- ),
192- 'retry '
193- );
194- }
202+ if (!$ value ) {
203+ return ;
204+ }
205+
206+ $ config = RetryConfigurationProvider::unwrap ($ value );
207+
208+ if ($ config ->getMode () === 'legacy ' ) {
209+ self ::appendLegacyModeRetries ($ value , $ config , $ args , $ list );
210+ return ;
211+ }
212+
213+ if (NewRetriesOptIn::isEnabled ()) {
214+ self ::appendStandardModeRetriesNew ($ config , $ args , $ list );
215+ return ;
195216 }
217+
218+ self ::appendStandardModeRetries ($ config , $ args , $ list );
219+ }
220+
221+ private static function appendLegacyModeRetries (
222+ $ value ,
223+ RetryConfigurationInterface $ config ,
224+ array &$ args ,
225+ HandlerList $ list
226+ ): void {
227+ $ maxRetries = self ::resolveLegacyModeMaxRetries ($ value , $ config );
228+
229+ $ list ->appendSign (
230+ Middleware::retry (
231+ RetryMiddleware::createDefaultDecider (
232+ $ maxRetries ,
233+ ['error_codes ' => ['TransactionInProgressException ' ]]
234+ ),
235+ function ($ retries ) {
236+ return $ retries
237+ ? RetryMiddleware::exponentialDelay ($ retries ) / 2
238+ : 0 ;
239+ },
240+ isset ($ args ['stats ' ]['retries ' ]) ? (bool ) $ args ['stats ' ]['retries ' ] : false
241+ ),
242+ 'retry '
243+ );
244+ }
245+
246+ private static function resolveLegacyModeMaxRetries (
247+ $ value ,
248+ RetryConfigurationInterface $ config
249+ ): int {
250+ if (
251+ NewRetriesOptIn::isEnabled ()
252+ && is_array ($ value )
253+ && !isset ($ value ['max_attempts ' ])
254+ ) {
255+ return self ::DEFAULT_LEGACY_MAX_ATTEMPTS ;
256+ }
257+
258+ return $ config ->getMaxAttempts () - 1 ;
259+ }
260+
261+ private static function appendStandardModeRetries (
262+ RetryConfigurationInterface $ config ,
263+ array &$ args ,
264+ HandlerList $ list
265+ ): void {
266+ $ list ->appendSign (
267+ RetryMiddlewareV2::wrap (
268+ $ config ,
269+ [
270+ 'collect_stats ' => $ args ['stats ' ]['retries ' ],
271+ 'transient_error_codes ' => ['TransactionInProgressException ' ],
272+ ]
273+ ),
274+ 'retry '
275+ );
276+ }
277+
278+ private static function appendStandardModeRetriesNew (
279+ RetryConfigurationInterface $ config ,
280+ array &$ args ,
281+ HandlerList $ list
282+ ): void {
283+ $ list ->appendSign (
284+ StandardRetryMiddleware::wrap (
285+ $ config ,
286+ [
287+ 'collect_stats ' => $ args ['stats ' ]['retries ' ],
288+ 'service ' => $ args ['service ' ],
289+ 'base_delay ' => self ::DEFAULT_BASE_DELAY_MS ,
290+ 'transient_error_codes ' => ['TransactionInProgressException ' ],
291+ ]
292+ ),
293+ 'retry '
294+ );
196295 }
197296
198297 /** @internal */
0 commit comments