Skip to content

Commit 329cd60

Browse files
committed
Updated retry policy behavior.
1. Added support for "retry modes". A retry mode allows configuring multiple SDK parameters at once using default retry profiles, some of which are standardized between AWS SDK languages. See RetryMode javadoc for more information. 2. Added the ability to configure or disable the default retry throttling behavior of the SDK that 'kicks in' during a large volume of retriable service call errors. This behavior can now be configured via `RetryPolicy.retryCapacityCondition`. 3. Fixed an issue where the retry condition returned by `RetryPolicy.retryCondition` differed from the one specified by `RetryPolicy.Builder.retryCondition`. The old value can be accessed via the new `RetryPolicy.aggregateRetryCondition`. 4. Fixed an issue where specifying your own retry policy would override AWS and service-specific retry conditions. By default, all retry policies now have AWS and service-specific retry conditions added. This can be disabled via the new `RetryPolicy.furtherRefinementsAllowed(false)`.
1 parent 3c21a57 commit 329cd60

File tree

72 files changed

+2697
-624
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

72 files changed

+2697
-624
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"type": "bugfix",
3+
"category": "AWS SDK for Java v2",
4+
"description": "Fixed an issue where the retry condition returned by `RetryPolicy.retryCondition` differed from the one specified by `RetryPolicy.Builder.retryCondition`. The old value can be accessed via the new `RetryPolicy.aggregateRetryCondition`."
5+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"type": "bugfix",
3+
"category": "AWS SDK for Java v2",
4+
"description": "Fixed an issue where specifying your own retry policy would override AWS and service-specific retry conditions. By default, all retry policies now have AWS and service-specific retry conditions added. This can be disabled via the new `RetryPolicy.furtherRefinementsAllowed(false)`."
5+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"type": "feature",
3+
"category": "AWS SDK for Java v2",
4+
"description": "Added the ability to configure or disable the default retry throttling behavior of the SDK that 'kicks in' during a large volume of retriable service call errors. This behavior can now be configured via `RetryPolicy.retryCapacityCondition`."
5+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"type": "feature",
3+
"category": "AWS SDK for Java v2",
4+
"description": "Added support for \"retry modes\". A retry mode allows configuring multiple SDK parameters at once using default retry profiles, some of which are standardized between AWS SDK languages. See RetryMode javadoc for more information."
5+
}

codegen/src/main/java/software/amazon/awssdk/codegen/poet/builder/BaseClientBuilderClass.java

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828
import com.squareup.javapoet.TypeName;
2929
import com.squareup.javapoet.TypeSpec;
3030
import com.squareup.javapoet.TypeVariableName;
31-
3231
import java.util.Collections;
3332
import java.util.List;
3433
import javax.lang.model.element.Modifier;
@@ -160,7 +159,7 @@ private MethodSpec mergeServiceDefaultsMethod() {
160159
SdkClientOption.class, crc32FromCompressedDataEnabled);
161160

162161
if (StringUtils.isNotBlank(model.getCustomizationConfig().getCustomRetryPolicy())) {
163-
builder.addCode(".option($T.RETRY_POLICY, $T.defaultPolicy())", SdkClientOption.class,
162+
builder.addCode(".option($T.RETRY_POLICY, $T.defaultRetryPolicy())", SdkClientOption.class,
164163
PoetUtils.classNameFromFqcn(model.getCustomizationConfig().getCustomRetryPolicy()));
165164
}
166165
builder.addCode(");");
@@ -198,14 +197,20 @@ private MethodSpec finalizeServiceConfigurationMethod() {
198197
.endControlFlow();
199198

200199
builder.addCode("return config.toBuilder()\n" +
201-
" .option($1T.EXECUTION_INTERCEPTORS, interceptors)\n" +
202-
" .option($1T.ENDPOINT_DISCOVERY_ENABLED, endpointDiscoveryEnabled)\n" +
203-
" .build();", SdkClientOption.class);
200+
".option($T.ENDPOINT_DISCOVERY_ENABLED, endpointDiscoveryEnabled)\n",
201+
SdkClientOption.class);
204202
} else {
205-
builder.addCode("return config.toBuilder()\n" +
206-
" .option($T.EXECUTION_INTERCEPTORS, interceptors)\n" +
207-
" .build();", SdkClientOption.class);
203+
builder.addCode("return config.toBuilder()\n");
204+
}
205+
206+
builder.addCode(".option($1T.EXECUTION_INTERCEPTORS, interceptors)", SdkClientOption.class);
207+
208+
if (StringUtils.isNotBlank(model.getCustomizationConfig().getCustomRetryPolicy())) {
209+
builder.addCode(".option($1T.RETRY_POLICY, $2T.addRetryConditions(config.option($1T.RETRY_POLICY)))",
210+
SdkClientOption.class,
211+
PoetUtils.classNameFromFqcn(model.getCustomizationConfig().getCustomRetryPolicy()));
208212
}
213+
builder.addCode(".build();");
209214

210215
return builder.build();
211216
}

codegen/src/test/java/software/amazon/awssdk/codegen/poet/builder/BuilderClassTest.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,11 @@ public void baseClientBuilderClass() throws Exception {
3838
validateGeneration(BaseClientBuilderClass::new, "test-client-builder-class.java");
3939
}
4040

41+
@Test
42+
public void baseQueryClientBuilderClass() throws Exception {
43+
validateQueryGeneration(BaseClientBuilderClass::new, "test-query-client-builder-class.java");
44+
}
45+
4146
@Test
4247
public void syncClientBuilderInterface() throws Exception {
4348
validateGeneration(SyncClientBuilderInterface::new, "test-sync-client-builder-interface.java");
@@ -61,4 +66,8 @@ public void asyncClientBuilderClass() throws Exception {
6166
private void validateGeneration(Function<IntermediateModel, ClassSpec> generatorConstructor, String expectedClassName) {
6267
assertThat(generatorConstructor.apply(ClientTestModels.jsonServiceModels()), generatesTo(expectedClassName));
6368
}
69+
70+
private void validateQueryGeneration(Function<IntermediateModel, ClassSpec> generatorConstructor, String expectedClassName) {
71+
assertThat(generatorConstructor.apply(ClientTestModels.queryServiceModels()), generatesTo(expectedClassName));
72+
}
6473
}

codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-client-builder-class.java

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import java.util.List;
44
import software.amazon.MyServiceHttpConfig;
5+
import software.amazon.MyServiceRetryPolicy;
56
import software.amazon.awssdk.annotations.Generated;
67
import software.amazon.awssdk.annotations.SdkInternalApi;
78
import software.amazon.awssdk.auth.signer.Aws4Signer;
@@ -33,8 +34,9 @@ protected final String serviceName() {
3334

3435
@Override
3536
protected final SdkClientConfiguration mergeServiceDefaults(SdkClientConfiguration config) {
36-
return config.merge(c -> c.option(SdkAdvancedClientOption.SIGNER, defaultSigner()).option(
37-
SdkClientOption.CRC32_FROM_COMPRESSED_DATA_ENABLED, false));
37+
return config.merge(c -> c.option(SdkAdvancedClientOption.SIGNER, defaultSigner())
38+
.option(SdkClientOption.CRC32_FROM_COMPRESSED_DATA_ENABLED, false)
39+
.option(SdkClientOption.RETRY_POLICY, MyServiceRetryPolicy.defaultRetryPolicy()));
3840
}
3941

4042
@Override
@@ -43,7 +45,11 @@ protected final SdkClientConfiguration finalizeServiceConfiguration(SdkClientCon
4345
List<ExecutionInterceptor> interceptors = interceptorFactory
4446
.getInterceptors("software/amazon/awssdk/services/json/execution.interceptors");
4547
interceptors = CollectionUtils.mergeLists(interceptors, config.option(SdkClientOption.EXECUTION_INTERCEPTORS));
46-
return config.toBuilder().option(SdkClientOption.EXECUTION_INTERCEPTORS, interceptors).build();
48+
return config
49+
.toBuilder()
50+
.option(SdkClientOption.EXECUTION_INTERCEPTORS, interceptors)
51+
.option(SdkClientOption.RETRY_POLICY,
52+
MyServiceRetryPolicy.addRetryConditions(config.option(SdkClientOption.RETRY_POLICY))).build();
4753
}
4854

4955
private Signer defaultSigner() {
@@ -70,4 +76,3 @@ protected final AttributeMap serviceHttpConfig() {
7076
return result;
7177
}
7278
}
73-
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package software.amazon.awssdk.services.query;
2+
3+
import java.util.Collections;
4+
import java.util.List;
5+
import software.amazon.awssdk.annotations.Generated;
6+
import software.amazon.awssdk.annotations.SdkInternalApi;
7+
import software.amazon.awssdk.auth.signer.Aws4Signer;
8+
import software.amazon.awssdk.awscore.client.builder.AwsDefaultClientBuilder;
9+
import software.amazon.awssdk.core.client.config.SdkAdvancedClientOption;
10+
import software.amazon.awssdk.core.client.config.SdkClientConfiguration;
11+
import software.amazon.awssdk.core.client.config.SdkClientOption;
12+
import software.amazon.awssdk.core.interceptor.ClasspathInterceptorChainFactory;
13+
import software.amazon.awssdk.core.interceptor.ExecutionInterceptor;
14+
import software.amazon.awssdk.core.signer.Signer;
15+
import software.amazon.awssdk.protocols.query.interceptor.QueryParametersToBodyInterceptor;
16+
import software.amazon.awssdk.utils.CollectionUtils;
17+
18+
/**
19+
* Internal base class for {@link DefaultQueryClientBuilder} and {@link DefaultQueryAsyncClientBuilder}.
20+
*/
21+
@Generated("software.amazon.awssdk:codegen")
22+
@SdkInternalApi
23+
abstract class DefaultQueryBaseClientBuilder<B extends QueryBaseClientBuilder<B, C>, C> extends AwsDefaultClientBuilder<B, C> {
24+
@Override
25+
protected final String serviceEndpointPrefix() {
26+
return "query-service";
27+
}
28+
29+
@Override
30+
protected final String serviceName() {
31+
return "Query";
32+
}
33+
34+
@Override
35+
protected final SdkClientConfiguration mergeServiceDefaults(SdkClientConfiguration config) {
36+
return config.merge(c -> c.option(SdkAdvancedClientOption.SIGNER, defaultSigner()).option(
37+
SdkClientOption.CRC32_FROM_COMPRESSED_DATA_ENABLED, false));
38+
}
39+
40+
@Override
41+
protected final SdkClientConfiguration finalizeServiceConfiguration(SdkClientConfiguration config) {
42+
ClasspathInterceptorChainFactory interceptorFactory = new ClasspathInterceptorChainFactory();
43+
List<ExecutionInterceptor> interceptors = interceptorFactory
44+
.getInterceptors("software/amazon/awssdk/services/query/execution.interceptors");
45+
interceptors = CollectionUtils.mergeLists(interceptors, config.option(SdkClientOption.EXECUTION_INTERCEPTORS));
46+
List<ExecutionInterceptor> protocolInterceptors = Collections.singletonList(new QueryParametersToBodyInterceptor());
47+
interceptors = CollectionUtils.mergeLists(interceptors, protocolInterceptors);
48+
return config.toBuilder().option(SdkClientOption.EXECUTION_INTERCEPTORS, interceptors).build();
49+
}
50+
51+
private Signer defaultSigner() {
52+
return Aws4Signer.create();
53+
}
54+
55+
@Override
56+
protected final String signingName() {
57+
return "query-service";
58+
}
59+
}

codegen/src/test/resources/software/amazon/awssdk/codegen/poet/client/c2j/json/customization.config

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
"presignersFqcn": "software.amazon.awssdk.services.acm.presign.AcmClientPresigners",
66
"serviceSpecificHttpConfig": "software.amazon.MyServiceHttpConfig",
77
"serviceSpecificClientConfigClass": "ServiceConfiguration",
8+
"customRetryPolicy": "software.amazon.MyServiceRetryPolicy",
89
"verifiedSimpleMethods" : ["paginatedOperationWithResultKey"],
910
"blacklistedSimpleMethods" : [
1011
"eventStreamOperation"

core/auth/src/main/java/software/amazon/awssdk/auth/credentials/internal/LazyAwsCredentialsProvider.java

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import software.amazon.awssdk.auth.credentials.AwsCredentials;
2121
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
2222
import software.amazon.awssdk.utils.IoUtils;
23+
import software.amazon.awssdk.utils.Lazy;
2324
import software.amazon.awssdk.utils.SdkAutoCloseable;
2425
import software.amazon.awssdk.utils.ToString;
2526

@@ -29,11 +30,10 @@
2930
*/
3031
@SdkInternalApi
3132
public class LazyAwsCredentialsProvider implements AwsCredentialsProvider, SdkAutoCloseable {
32-
private final Supplier<AwsCredentialsProvider> delegateConstructor;
33-
private volatile AwsCredentialsProvider delegate;
33+
private final Lazy<AwsCredentialsProvider> delegate;
3434

3535
private LazyAwsCredentialsProvider(Supplier<AwsCredentialsProvider> delegateConstructor) {
36-
this.delegateConstructor = delegateConstructor;
36+
this.delegate = new Lazy<>(delegateConstructor);
3737
}
3838

3939
public static LazyAwsCredentialsProvider create(Supplier<AwsCredentialsProvider> delegateConstructor) {
@@ -42,14 +42,7 @@ public static LazyAwsCredentialsProvider create(Supplier<AwsCredentialsProvider>
4242

4343
@Override
4444
public AwsCredentials resolveCredentials() {
45-
if (delegate == null) {
46-
synchronized (this) {
47-
if (delegate == null) {
48-
delegate = delegateConstructor.get();
49-
}
50-
}
51-
}
52-
return delegate.resolveCredentials();
45+
return delegate.getValue().resolveCredentials();
5346
}
5447

5548
@Override
@@ -60,7 +53,6 @@ public void close() {
6053
@Override
6154
public String toString() {
6255
return ToString.builder("LazyAwsCredentialsProvider")
63-
.add("delegateConstructor", delegateConstructor)
6456
.add("delegate", delegate)
6557
.build();
6658
}

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

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import software.amazon.awssdk.core.client.config.SdkClientConfiguration;
3434
import software.amazon.awssdk.core.client.config.SdkClientOption;
3535
import software.amazon.awssdk.core.interceptor.ExecutionInterceptor;
36+
import software.amazon.awssdk.core.retry.RetryPolicy;
3637
import software.amazon.awssdk.http.SdkHttpClient;
3738
import software.amazon.awssdk.http.async.SdkAsyncHttpClient;
3839
import software.amazon.awssdk.regions.Region;
@@ -113,10 +114,14 @@ protected AttributeMap serviceHttpConfig() {
113114
@Override
114115
protected final SdkClientConfiguration mergeChildDefaults(SdkClientConfiguration configuration) {
115116
SdkClientConfiguration config = mergeServiceDefaults(configuration);
117+
116118
return config.merge(c -> c.option(AwsClientOption.AWS_REGION, resolveRegion(config))
117119
.option(AwsAdvancedClientOption.ENABLE_DEFAULT_REGION_DETECTION, true)
118120
.option(AwsClientOption.CREDENTIALS_PROVIDER, DefaultCredentialsProvider.create())
119-
.option(SdkClientOption.RETRY_POLICY, AwsRetryPolicy.defaultRetryPolicy())
121+
.option(SdkClientOption.RETRY_POLICY, AwsRetryPolicy.defaultRetryPolicy()
122+
.toBuilder()
123+
.additionalRetryConditionsAllowed(false)
124+
.build())
120125
.option(SdkAdvancedClientOption.DISABLE_HOST_PREFIX_INJECTION, false)
121126
.option(AwsClientOption.SERVICE_SIGNING_NAME, signingName())
122127
.option(SdkClientOption.SERVICE_NAME, serviceName())
@@ -137,6 +142,7 @@ protected final SdkClientConfiguration finalizeChildConfiguration(SdkClientConfi
137142
.option(SdkClientOption.ENDPOINT, resolveEndpoint(configuration))
138143
.option(SdkClientOption.EXECUTION_INTERCEPTORS, addAwsInterceptors(configuration))
139144
.option(AwsClientOption.SIGNING_REGION, resolveSigningRegion(configuration))
145+
.option(SdkClientOption.RETRY_POLICY, resolveRetryPolicy(configuration))
140146
.build();
141147
return finalizeServiceConfiguration(config);
142148
}
@@ -186,6 +192,11 @@ private Region regionFromDefaultProvider(SdkClientConfiguration config) {
186192
return DEFAULT_REGION_PROVIDER.getRegion();
187193
}
188194

195+
private RetryPolicy resolveRetryPolicy(SdkClientConfiguration configuration) {
196+
RetryPolicy policy = configuration.option(SdkClientOption.RETRY_POLICY);
197+
return policy.additionalRetryConditionsAllowed() ? AwsRetryPolicy.addRetryConditions(policy) : policy;
198+
}
199+
189200
@Override
190201
public final BuilderT region(Region region) {
191202
clientConfiguration.option(AwsClientOption.AWS_REGION, region);

core/aws-core/src/main/java/software/amazon/awssdk/awscore/retry/AwsRetryPolicy.java

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import software.amazon.awssdk.annotations.SdkPublicApi;
1919
import software.amazon.awssdk.awscore.internal.AwsErrorCode;
2020
import software.amazon.awssdk.awscore.retry.conditions.RetryOnErrorCodeCondition;
21+
import software.amazon.awssdk.core.retry.RetryMode;
2122
import software.amazon.awssdk.core.retry.RetryPolicy;
2223
import software.amazon.awssdk.core.retry.conditions.OrRetryCondition;
2324
import software.amazon.awssdk.core.retry.conditions.RetryCondition;
@@ -31,12 +32,38 @@ public final class AwsRetryPolicy {
3132
private AwsRetryPolicy() {
3233
}
3334

35+
/**
36+
* Retrieve the {@link RetryCondition#defaultRetryCondition()} with AWS-specific conditions added.
37+
*/
3438
public static RetryCondition defaultRetryCondition() {
35-
return OrRetryCondition.create(RetryCondition.defaultRetryCondition(),
36-
RetryOnErrorCodeCondition.create(AwsErrorCode.RETRYABLE_ERROR_CODES));
39+
return OrRetryCondition.create(RetryCondition.defaultRetryCondition(), awsRetryCondition());
3740
}
3841

42+
/**
43+
* Retrieve the {@link RetryPolicy#defaultRetryPolicy()} with AWS-specific conditions added.
44+
*/
3945
public static RetryPolicy defaultRetryPolicy() {
40-
return RetryPolicy.defaultRetryPolicy().toBuilder().retryCondition(defaultRetryCondition()).build();
46+
return forRetryMode(RetryMode.defaultRetryMode());
47+
}
48+
49+
/**
50+
* Retrieve the {@link RetryPolicy#defaultRetryPolicy()} with AWS-specific conditions added. This uses the specified
51+
* {@link RetryMode} when constructing the {@link RetryPolicy}.
52+
*/
53+
public static RetryPolicy forRetryMode(RetryMode retryMode) {
54+
return addRetryConditions(RetryPolicy.forRetryMode(retryMode));
55+
}
56+
57+
/**
58+
* Update the provided {@link RetryPolicy} to add AWS-specific conditions.
59+
*/
60+
public static RetryPolicy addRetryConditions(RetryPolicy condition) {
61+
return condition.toBuilder()
62+
.retryCondition(OrRetryCondition.create(condition.retryCondition(), awsRetryCondition()))
63+
.build();
64+
}
65+
66+
private static RetryOnErrorCodeCondition awsRetryCondition() {
67+
return RetryOnErrorCodeCondition.create(AwsErrorCode.RETRYABLE_ERROR_CODES);
4168
}
4269
}

core/aws-core/src/test/java/software/amazon/awssdk/awscore/client/utils/HttpTestUtils.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@
2121
import java.util.concurrent.Executors;
2222
import software.amazon.awssdk.core.client.config.SdkAdvancedAsyncClientOption;
2323
import software.amazon.awssdk.core.client.config.SdkAdvancedClientOption;
24-
import software.amazon.awssdk.core.client.config.SdkClientOption;
2524
import software.amazon.awssdk.core.client.config.SdkClientConfiguration;
25+
import software.amazon.awssdk.core.client.config.SdkClientOption;
2626
import software.amazon.awssdk.core.internal.http.AmazonSyncHttpClient;
2727
import software.amazon.awssdk.core.internal.http.loader.DefaultSdkHttpClientBuilder;
2828
import software.amazon.awssdk.core.retry.RetryPolicy;

core/profiles/src/main/java/software/amazon/awssdk/profiles/ProfileFile.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,10 @@ public static Aggregator aggregator() {
8080
* Get the default profile file, using the credentials file from "~/.aws/credentials", the config file from "~/.aws/config"
8181
* and the "default" profile. This default behavior can be customized using the
8282
* {@link ProfileFileSystemSetting#AWS_SHARED_CREDENTIALS_FILE}, {@link ProfileFileSystemSetting#AWS_CONFIG_FILE} and
83-
* {@link ProfileFileSystemSetting#AWS_PROFILE} settings or by specifying a different profile file and profile name
83+
* {@link ProfileFileSystemSetting#AWS_PROFILE} settings or by specifying a different profile file and profile name.
84+
*
85+
* <p>
86+
* The file is read each time this method is invoked.
8487
*/
8588
public static ProfileFile defaultProfileFile() {
8689
return ProfileFile.aggregator()

core/profiles/src/main/java/software/amazon/awssdk/profiles/ProfileProperty.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,5 +93,11 @@ public final class ProfileProperty {
9393
*/
9494
public static final String S3_US_EAST_1_REGIONAL_ENDPOINT = "s3_us_east_1_regional_endpoint";
9595

96+
/**
97+
* The "retry mode" to be used for clients created using the currently-configured profile. Values supported by all SDKs are
98+
* "legacy" and "standard". See the {@code RetryMode} class JavaDoc for more information.
99+
*/
100+
public static final String RETRY_MODE = "retry_mode";
101+
96102
private ProfileProperty() {}
97103
}

core/regions/src/main/java/software/amazon/awssdk/regions/providers/AwsProfileRegionProvider.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@
2727
*/
2828
@SdkProtectedApi
2929
public final class AwsProfileRegionProvider implements AwsRegionProvider {
30-
3130
private final String profileName = ProfileFileSystemSetting.AWS_PROFILE.getStringValueOrThrow();
3231

3332
@Override

0 commit comments

Comments
 (0)