Skip to content

Commit 811c9b1

Browse files
authored
Add new sync and async retryable stages (#4062)
* Add new sync and async retryable stages * Address PR comments
1 parent 03817c3 commit 811c9b1

File tree

28 files changed

+1283
-23
lines changed

28 files changed

+1283
-23
lines changed

core/aws-core/pom.xml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,16 @@
7878
<artifactId>utils</artifactId>
7979
<version>${awsjavasdk.version}</version>
8080
</dependency>
81+
<dependency>
82+
<groupId>software.amazon.awssdk</groupId>
83+
<artifactId>retries-api</artifactId>
84+
<version>${awsjavasdk.version}</version>
85+
</dependency>
86+
<dependency>
87+
<groupId>software.amazon.awssdk</groupId>
88+
<artifactId>retries</artifactId>
89+
<version>${awsjavasdk.version}</version>
90+
</dependency>
8191
<dependency>
8292
<groupId>software.amazon.eventstream</groupId>
8393
<artifactId>eventstream</artifactId>

core/aws-core/src/main/java/software/amazon/awssdk/awscore/client/builder/AwsDefaultClientBuilder.java

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
import software.amazon.awssdk.awscore.internal.defaultsmode.DefaultsModeConfiguration;
4040
import software.amazon.awssdk.awscore.internal.defaultsmode.DefaultsModeResolver;
4141
import software.amazon.awssdk.awscore.retry.AwsRetryPolicy;
42+
import software.amazon.awssdk.awscore.retry.AwsRetryStrategy;
4243
import software.amazon.awssdk.core.client.builder.SdkDefaultClientBuilder;
4344
import software.amazon.awssdk.core.client.config.SdkAdvancedClientOption;
4445
import software.amazon.awssdk.core.client.config.SdkClientConfiguration;
@@ -53,6 +54,7 @@
5354
import software.amazon.awssdk.regions.ServiceMetadata;
5455
import software.amazon.awssdk.regions.ServiceMetadataAdvancedOption;
5556
import software.amazon.awssdk.regions.providers.DefaultAwsRegionProviderChain;
57+
import software.amazon.awssdk.retries.api.RetryStrategy;
5658
import software.amazon.awssdk.utils.AttributeMap;
5759
import software.amazon.awssdk.utils.CollectionUtils;
5860
import software.amazon.awssdk.utils.Logger;
@@ -196,6 +198,7 @@ protected final SdkClientConfiguration finalizeChildConfiguration(SdkClientConfi
196198
.option(SdkClientOption.EXECUTION_INTERCEPTORS, addAwsInterceptors(configuration))
197199
.option(AwsClientOption.SIGNING_REGION, resolveSigningRegion(configuration))
198200
.option(SdkClientOption.RETRY_POLICY, resolveAwsRetryPolicy(configuration))
201+
.option(SdkClientOption.RETRY_STRATEGY, resolveAwsRetryStrategy(configuration))
199202
.build();
200203
}
201204

@@ -375,6 +378,35 @@ private RetryPolicy resolveAwsRetryPolicy(SdkClientConfiguration config) {
375378
.defaultRetryMode(config.option(SdkClientOption.DEFAULT_RETRY_MODE))
376379
.resolve();
377380
return AwsRetryPolicy.forRetryMode(retryMode);
381+
// TODO: fixme This will be changed like this to pick the configured retry strategy
382+
// if no retry policy is configured.
383+
/*
384+
RetryPolicy policy = config.option(SdkClientOption.RETRY_POLICY);
385+
386+
if (policy != null) {
387+
if (policy.additionalRetryConditionsAllowed()) {
388+
return AwsRetryPolicy.addRetryConditions(policy);
389+
} else {
390+
return policy;
391+
}
392+
}
393+
394+
// If we don't have a configured retry policy we will use the configured retry strategy instead.
395+
return null;
396+
*/
397+
}
398+
399+
private RetryStrategy<?, ?> resolveAwsRetryStrategy(SdkClientConfiguration config) {
400+
RetryStrategy<?, ?> strategy = config.option(SdkClientOption.RETRY_STRATEGY);
401+
if (strategy != null) {
402+
return AwsRetryStrategy.addRetryConditions(strategy);
403+
}
404+
RetryMode retryMode = RetryMode.resolver()
405+
.profileFile(config.option(SdkClientOption.PROFILE_FILE_SUPPLIER))
406+
.profileName(config.option(SdkClientOption.PROFILE_NAME))
407+
.defaultRetryMode(config.option(SdkClientOption.DEFAULT_RETRY_MODE))
408+
.resolve();
409+
return AwsRetryStrategy.forRetryMode(retryMode);
378410
}
379411

380412
@Override
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License").
5+
* You may not use this file except in compliance with the License.
6+
* A copy of the License is located at
7+
*
8+
* http://aws.amazon.com/apache2.0
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed
11+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
* express or implied. See the License for the specific language governing
13+
* permissions and limitations under the License.
14+
*/
15+
16+
package software.amazon.awssdk.awscore.retry;
17+
18+
import software.amazon.awssdk.annotations.SdkPublicApi;
19+
import software.amazon.awssdk.awscore.exception.AwsServiceException;
20+
import software.amazon.awssdk.awscore.internal.AwsErrorCode;
21+
import software.amazon.awssdk.core.internal.retry.SdkDefaultRetryStrategy;
22+
import software.amazon.awssdk.core.retry.RetryMode;
23+
import software.amazon.awssdk.retries.AdaptiveRetryStrategy;
24+
import software.amazon.awssdk.retries.DefaultRetryStrategy;
25+
import software.amazon.awssdk.retries.LegacyRetryStrategy;
26+
import software.amazon.awssdk.retries.StandardRetryStrategy;
27+
import software.amazon.awssdk.retries.api.RetryStrategy;
28+
29+
/**
30+
* Retry strategies used by clients when communicating with AWS services.
31+
*/
32+
@SdkPublicApi
33+
public final class AwsRetryStrategy {
34+
35+
private AwsRetryStrategy() {
36+
}
37+
38+
/**
39+
* Retrieve the {@link SdkDefaultRetryStrategy#defaultRetryStrategy()} with AWS-specific conditions added.
40+
*
41+
* @return The default retry strategy.
42+
*/
43+
public static RetryStrategy<?, ?> defaultRetryStrategy() {
44+
return forRetryMode(RetryMode.defaultRetryMode());
45+
}
46+
47+
/**
48+
* Retrieve the appropriate retry strategy for the retry mode with AWS-specific conditions added.
49+
*
50+
* @param mode The retry mode for which we want to create a retry strategy.
51+
* @return A retry strategy for the given retry mode.
52+
*/
53+
public static RetryStrategy<?, ?> forRetryMode(RetryMode mode) {
54+
switch (mode) {
55+
case STANDARD:
56+
return standardRetryStrategy();
57+
case ADAPTIVE:
58+
return adaptiveRetryStrategy();
59+
case LEGACY:
60+
return legacyRetryStrategy();
61+
default:
62+
throw new IllegalArgumentException("unknown retry mode: " + mode);
63+
}
64+
}
65+
66+
/**
67+
* Update the provided {@link RetryStrategy} to add AWS-specific conditions.
68+
*
69+
* @param strategy The strategy to update
70+
* @return The updated strategy
71+
*/
72+
public static RetryStrategy<?, ?> addRetryConditions(RetryStrategy<?, ?> strategy) {
73+
return strategy.toBuilder()
74+
.retryOnException(AwsRetryStrategy::retryOnAwsRetryableErrors)
75+
.build();
76+
}
77+
78+
/**
79+
* Returns a retry strategy that does not retry.
80+
*
81+
* @return A retry strategy that does not retry.
82+
*/
83+
public static RetryStrategy<?, ?> none() {
84+
return DefaultRetryStrategy.none();
85+
}
86+
87+
88+
/**
89+
* Returns a {@link StandardRetryStrategy} with AWS-specific conditions added.
90+
*
91+
* @return A {@link StandardRetryStrategy} with AWS-specific conditions added.
92+
*/
93+
public static StandardRetryStrategy standardRetryStrategy() {
94+
StandardRetryStrategy.Builder builder = SdkDefaultRetryStrategy.standardRetryStrategyBuilder();
95+
return configure(builder).build();
96+
}
97+
98+
/**
99+
* Returns a {@link LegacyRetryStrategy} with AWS-specific conditions added.
100+
*
101+
* @return A {@link LegacyRetryStrategy} with AWS-specific conditions added.
102+
*/
103+
public static LegacyRetryStrategy legacyRetryStrategy() {
104+
LegacyRetryStrategy.Builder builder = SdkDefaultRetryStrategy.legacyRetryStrategyBuilder();
105+
return configure(builder)
106+
.build();
107+
}
108+
109+
/**
110+
* Returns an {@link AdaptiveRetryStrategy} with AWS-specific conditions added.
111+
*
112+
* @return An {@link AdaptiveRetryStrategy} with AWS-specific conditions added.
113+
*/
114+
public static AdaptiveRetryStrategy adaptiveRetryStrategy() {
115+
AdaptiveRetryStrategy.Builder builder = SdkDefaultRetryStrategy.adaptiveRetryStrategyBuilder();
116+
return configure(builder)
117+
.build();
118+
}
119+
120+
/**
121+
* Configures a retry strategy using its builder to add AWS-specific retry exceptions.
122+
*
123+
* @param builder The builder to add the AWS-specific retry exceptions
124+
* @return The given builder
125+
* @param <T> The type of the builder extending {@link RetryStrategy.Builder}
126+
*/
127+
public static <T extends RetryStrategy.Builder<T, ?>> T configure(T builder) {
128+
return builder.retryOnException(AwsRetryStrategy::retryOnAwsRetryableErrors);
129+
}
130+
131+
private static boolean retryOnAwsRetryableErrors(Throwable ex) {
132+
if (ex instanceof AwsServiceException) {
133+
AwsServiceException exception = (AwsServiceException) ex;
134+
return AwsErrorCode.RETRYABLE_ERROR_CODES.contains(exception.awsErrorDetails().errorCode());
135+
}
136+
return false;
137+
}
138+
}

core/retries/src/main/java/software/amazon/awssdk/retries/DefaultRetryStrategy.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,15 @@ public final class DefaultRetryStrategy {
2929
private DefaultRetryStrategy() {
3030
}
3131

32+
/**
33+
* Creates a non-retrying strategy.
34+
*/
35+
public static StandardRetryStrategy none() {
36+
return standardStrategyBuilder()
37+
.maxAttempts(1)
38+
.build();
39+
}
40+
3241
/**
3342
* Create a new builder for a {@link StandardRetryStrategy}.
3443
*

core/sdk-core/pom.xml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,16 @@
6161
<artifactId>profiles</artifactId>
6262
<version>${awsjavasdk.version}</version>
6363
</dependency>
64+
<dependency>
65+
<groupId>software.amazon.awssdk</groupId>
66+
<artifactId>retries-api</artifactId>
67+
<version>${awsjavasdk.version}</version>
68+
</dependency>
69+
<dependency>
70+
<groupId>software.amazon.awssdk</groupId>
71+
<artifactId>retries</artifactId>
72+
<version>${awsjavasdk.version}</version>
73+
</dependency>
6474
<dependency>
6575
<groupId>org.slf4j</groupId>
6676
<artifactId>slf4j-api</artifactId>

core/sdk-core/src/main/java/software/amazon/awssdk/core/client/builder/SdkDefaultClientBuilder.java

Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
import static software.amazon.awssdk.core.client.config.SdkClientOption.PROFILE_FILE_SUPPLIER;
4040
import static software.amazon.awssdk.core.client.config.SdkClientOption.PROFILE_NAME;
4141
import static software.amazon.awssdk.core.client.config.SdkClientOption.RETRY_POLICY;
42+
import static software.amazon.awssdk.core.client.config.SdkClientOption.RETRY_STRATEGY;
4243
import static software.amazon.awssdk.core.client.config.SdkClientOption.SCHEDULED_EXECUTOR_SERVICE;
4344
import static software.amazon.awssdk.core.client.config.SdkClientOption.SIGNER_OVERRIDDEN;
4445
import static software.amazon.awssdk.core.client.config.SdkClientOption.SYNC_HTTP_CLIENT;
@@ -77,6 +78,7 @@
7778
import software.amazon.awssdk.core.internal.interceptor.HttpChecksumRequiredInterceptor;
7879
import software.amazon.awssdk.core.internal.interceptor.HttpChecksumValidationInterceptor;
7980
import software.amazon.awssdk.core.internal.interceptor.SyncHttpChecksumInTrailerInterceptor;
81+
import software.amazon.awssdk.core.internal.retry.SdkDefaultRetryStrategy;
8082
import software.amazon.awssdk.core.retry.RetryMode;
8183
import software.amazon.awssdk.core.retry.RetryPolicy;
8284
import software.amazon.awssdk.core.util.SdkUserAgent;
@@ -89,6 +91,10 @@
8991
import software.amazon.awssdk.profiles.ProfileFile;
9092
import software.amazon.awssdk.profiles.ProfileFileSupplier;
9193
import software.amazon.awssdk.profiles.ProfileFileSystemSetting;
94+
import software.amazon.awssdk.retries.AdaptiveRetryStrategy;
95+
import software.amazon.awssdk.retries.LegacyRetryStrategy;
96+
import software.amazon.awssdk.retries.StandardRetryStrategy;
97+
import software.amazon.awssdk.retries.api.RetryStrategy;
9298
import software.amazon.awssdk.utils.AttributeMap;
9399
import software.amazon.awssdk.utils.Either;
94100
import software.amazon.awssdk.utils.ScheduledExecutorUtils;
@@ -226,6 +232,7 @@ private SdkClientConfiguration setOverrides(SdkClientConfiguration configuration
226232
builder.option(SCHEDULED_EXECUTOR_SERVICE, clientOverrideConfiguration.scheduledExecutorService().orElse(null));
227233
builder.option(EXECUTION_INTERCEPTORS, clientOverrideConfiguration.executionInterceptors());
228234
builder.option(RETRY_POLICY, clientOverrideConfiguration.retryPolicy().orElse(null));
235+
builder.option(RETRY_STRATEGY, clientOverrideConfiguration.retryStrategy().orElse(null));
229236
builder.option(ADDITIONAL_HTTP_HEADERS, clientOverrideConfiguration.headers());
230237
builder.option(SIGNER, clientOverrideConfiguration.advancedOption(SIGNER).orElse(null));
231238
builder.option(USER_AGENT_SUFFIX, clientOverrideConfiguration.advancedOption(USER_AGENT_SUFFIX).orElse(null));
@@ -314,21 +321,40 @@ private SdkClientConfiguration finalizeAsyncConfiguration(SdkClientConfiguration
314321
*/
315322
private SdkClientConfiguration finalizeConfiguration(SdkClientConfiguration config) {
316323
RetryPolicy retryPolicy = resolveRetryPolicy(config);
324+
RetryStrategy<?, ?> retryStrategy = resolveRetryStrategy(config);
325+
String retryMode = resolveRetryMode(retryPolicy, retryStrategy);
317326
return config.toBuilder()
318327
.option(SCHEDULED_EXECUTOR_SERVICE, resolveScheduledExecutorService(config))
319328
.option(EXECUTION_INTERCEPTORS, resolveExecutionInterceptors(config))
320329
.option(RETRY_POLICY, retryPolicy)
321-
.option(CLIENT_USER_AGENT, resolveClientUserAgent(config, retryPolicy))
330+
.option(RETRY_STRATEGY, retryStrategy)
331+
.option(CLIENT_USER_AGENT, resolveClientUserAgent(config, retryMode))
322332
.build();
323333
}
324334

325-
private String resolveClientUserAgent(SdkClientConfiguration config, RetryPolicy retryPolicy) {
335+
private String resolveRetryMode(RetryPolicy retryPolicy, RetryStrategy<?, ?> retryStrategy) {
336+
if (retryPolicy != null) {
337+
return retryPolicy.retryMode().toString();
338+
}
339+
if (retryStrategy instanceof StandardRetryStrategy) {
340+
return RetryMode.STANDARD.toString();
341+
}
342+
if (retryStrategy instanceof LegacyRetryStrategy) {
343+
return RetryMode.LEGACY.toString();
344+
}
345+
if (retryStrategy instanceof AdaptiveRetryStrategy) {
346+
return RetryMode.ADAPTIVE.toString();
347+
}
348+
return "UnknownRetryMode";
349+
}
350+
351+
private String resolveClientUserAgent(SdkClientConfiguration config, String retryMode) {
326352
return ApplyUserAgentStage.resolveClientUserAgent(config.option(USER_AGENT_PREFIX),
327353
config.option(INTERNAL_USER_AGENT),
328354
config.option(CLIENT_TYPE),
329355
config.option(SYNC_HTTP_CLIENT),
330356
config.option(ASYNC_HTTP_CLIENT),
331-
retryPolicy);
357+
retryMode);
332358
}
333359

334360
private RetryPolicy resolveRetryPolicy(SdkClientConfiguration config) {
@@ -343,6 +369,25 @@ private RetryPolicy resolveRetryPolicy(SdkClientConfiguration config) {
343369
.defaultRetryMode(config.option(SdkClientOption.DEFAULT_RETRY_MODE))
344370
.resolve();
345371
return RetryPolicy.forRetryMode(retryMode);
372+
// TODO: fixme This will be changed like this to pick the configured retry strategy
373+
// if no retry policy is configured.
374+
/*
375+
return config.option(SdkClientOption.RETRY_POLICY);
376+
*/
377+
}
378+
379+
private RetryStrategy<?, ?> resolveRetryStrategy(SdkClientConfiguration config) {
380+
RetryStrategy<?, ?> strategy = config.option(RETRY_STRATEGY);
381+
if (strategy != null) {
382+
return strategy;
383+
}
384+
385+
RetryMode retryMode = RetryMode.resolver()
386+
.profileFile(config.option(SdkClientOption.PROFILE_FILE_SUPPLIER))
387+
.profileName(config.option(SdkClientOption.PROFILE_NAME))
388+
.defaultRetryMode(config.option(SdkClientOption.DEFAULT_RETRY_MODE))
389+
.resolve();
390+
return SdkDefaultRetryStrategy.forRetryMode(retryMode);
346391
}
347392

348393
/**

0 commit comments

Comments
 (0)