Skip to content

Commit 56645ac

Browse files
committed
Add new sync and async retryable stages
1 parent c434b26 commit 56645ac

File tree

29 files changed

+1248
-23
lines changed

29 files changed

+1248
-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+
// 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: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
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 class AwsRetryStrategy {
34+
35+
private AwsRetryStrategy() {
36+
}
37+
38+
/**
39+
* Retrieve the {@link SdkDefaultRetryStrategy#defaultRetryStrategy()} with AWS-specific conditions added.
40+
*/
41+
public static RetryStrategy<?, ?> defaultRetryStrategy() {
42+
return forRetryMode(RetryMode.defaultRetryMode());
43+
}
44+
45+
/**
46+
* Retrieve the appropriate retry strategy for the retry mode with AWS-specific conditions added.
47+
*/
48+
public static RetryStrategy<?, ?> forRetryMode(RetryMode mode) {
49+
switch (mode) {
50+
case STANDARD:
51+
return standardRetryStrategy();
52+
case ADAPTIVE:
53+
return adaptiveRetryStrategy();
54+
case LEGACY:
55+
return legacyRetryStrategy();
56+
default:
57+
throw new IllegalStateException("unknown retry mode: " + mode);
58+
}
59+
}
60+
61+
/**
62+
* Update the provided {@link RetryStrategy} to add AWS-specific conditions.
63+
*/
64+
public static RetryStrategy<?, ?> addRetryConditions(RetryStrategy<?, ?> strategy) {
65+
return strategy.toBuilder()
66+
.retryOnException(AwsRetryStrategy::retryOnAwsRetryableErrors)
67+
.build();
68+
}
69+
70+
/**
71+
* Returns a retry strategy that does not retry.
72+
*/
73+
public static RetryStrategy<?, ?> none() {
74+
return DefaultRetryStrategy.none();
75+
}
76+
77+
78+
/**
79+
* Returns a {@link StandardRetryStrategy} with AWS-specific conditions added.
80+
*/
81+
public static StandardRetryStrategy standardRetryStrategy() {
82+
StandardRetryStrategy.Builder builder = SdkDefaultRetryStrategy.standardRetryStrategyBuilder();
83+
return configure(builder).build();
84+
}
85+
86+
/**
87+
* Returns a {@link LegacyRetryStrategy} with AWS-specific conditions added.
88+
*/
89+
public static LegacyRetryStrategy legacyRetryStrategy() {
90+
LegacyRetryStrategy.Builder builder = SdkDefaultRetryStrategy.legacyRetryStrategyBuilder();
91+
return configure(builder)
92+
.build();
93+
}
94+
95+
/**
96+
* Returns an {@link AdaptiveRetryStrategy} with AWS-specific conditions added.
97+
*/
98+
public static AdaptiveRetryStrategy adaptiveRetryStrategy() {
99+
AdaptiveRetryStrategy.Builder builder = SdkDefaultRetryStrategy.adaptiveRetryStrategyBuilder();
100+
return configure(builder)
101+
.build();
102+
}
103+
104+
/**
105+
* Configures a retry strategy using its builder to add AWS-specific retry conditions.
106+
*/
107+
public static <T extends RetryStrategy.Builder<T, ?>> T configure(T builder) {
108+
return builder.retryOnException(AwsRetryStrategy::retryOnAwsRetryableErrors);
109+
}
110+
111+
private static boolean retryOnAwsRetryableErrors(Throwable ex) {
112+
if (ex instanceof AwsServiceException) {
113+
AwsServiceException exception = (AwsServiceException) ex;
114+
// fixme, I need to double check with the team whether this assumption that the error code will be in the [error
115+
// details -> error code] is correct.
116+
return AwsErrorCode.RETRYABLE_ERROR_CODES.contains(exception.awsErrorDetails().errorCode());
117+
}
118+
return false;
119+
}
120+
}

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+
// 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)