Skip to content

Commit 3572070

Browse files
committed
Factor new retries option in client builder
This commit updates the retry mode resolution behavior for SDK clients: - When resolving the RetryStrategy to use, also determine whether retry 2.1 behavior should be enabled for that retry strategy. - If the `newRetries2026Default` property is set in customization.config for a service, ensure that this is treated as the default option for `AWS_NEW_RETRIES_2026` when building the client if not set anywhere else.
1 parent b18babd commit 3572070

17 files changed

Lines changed: 547 additions & 46 deletions

File tree

codegen/src/main/java/software/amazon/awssdk/codegen/model/config/customization/CustomizationConfig.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,8 @@ public class CustomizationConfig {
225225

226226
private RetryMode defaultRetryMode;
227227

228+
private Boolean newRetries2026Default;
229+
228230
/**
229231
* Whether to generate an abstract decorator class that delegates to the async service client
230232
*/
@@ -714,6 +716,14 @@ public void setDefaultRetryMode(RetryMode defaultRetryMode) {
714716
this.defaultRetryMode = defaultRetryMode;
715717
}
716718

719+
public Boolean getNewRetries2026Default() {
720+
return newRetries2026Default;
721+
}
722+
723+
public void setNewRetries2026Default(Boolean newRetries2026Default) {
724+
this.newRetries2026Default = newRetries2026Default;
725+
}
726+
717727
public ServiceConfig getServiceConfig() {
718728
return serviceConfig;
719729
}

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -328,9 +328,10 @@ private void configureEnvironmentBearerToken(MethodSpec.Builder builder) {
328328
private Optional<MethodSpec> mergeInternalDefaultsMethod() {
329329
String userAgent = model.getCustomizationConfig().getUserAgent();
330330
RetryMode defaultRetryMode = model.getCustomizationConfig().getDefaultRetryMode();
331+
Boolean newRetries2026Default = model.getCustomizationConfig().getNewRetries2026Default();
331332

332333
// If none of the options are customized, then we do not need to bother overriding the method
333-
if (userAgent == null && defaultRetryMode == null) {
334+
if (userAgent == null && defaultRetryMode == null && newRetries2026Default == null) {
334335
return Optional.empty();
335336
}
336337

@@ -348,6 +349,10 @@ private Optional<MethodSpec> mergeInternalDefaultsMethod() {
348349
builder.addCode("c.option($T.DEFAULT_RETRY_MODE, $T.$L);\n",
349350
SdkClientOption.class, RetryMode.class, defaultRetryMode.name());
350351
}
352+
if (newRetries2026Default != null) {
353+
builder.addCode("c.option($T.NEW_RETRIES_2026_DEFAULT, $L);\n",
354+
SdkClientOption.class, newRetries2026Default);
355+
}
351356
builder.addCode("});\n");
352357
return Optional.of(builder.build());
353358
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ void baseClientBuilderClassWithNoAuthService_sra() {
6969
}
7070

7171
@Test
72-
void baseClientBuilderClassWithInternalUserAgent_sra() {
72+
void baseClientBuilderClassWithInternalDefaults_sra() {
7373
validateBaseClientBuilderClassGeneration(internalConfigModels(), "test-client-builder-internal-defaults-class.java");
7474
}
7575

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ protected final SdkClientConfiguration mergeInternalDefaults(SdkClientConfigurat
6969
return config.merge(c -> {
7070
c.option(SdkClientOption.INTERNAL_USER_AGENT, "md/foobar");
7171
c.option(SdkClientOption.DEFAULT_RETRY_MODE, RetryMode.STANDARD);
72+
c.option(SdkClientOption.NEW_RETRIES_2026_DEFAULT, true);
7273
});
7374
}
7475

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,6 @@
33
"skip" : true
44
},
55
"userAgent": "md/foobar",
6-
"defaultRetryMode": "STANDARD"
6+
"defaultRetryMode": "STANDARD",
7+
"newRetries2026Default": "true"
78
}

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

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
import software.amazon.awssdk.core.interceptor.ExecutionInterceptor;
5555
import software.amazon.awssdk.core.internal.SdkInternalTestAdvancedClientOption;
5656
import software.amazon.awssdk.core.internal.retry.SdkDefaultRetryStrategy;
57+
import software.amazon.awssdk.core.retry.NewRetries2026Resolver;
5758
import software.amazon.awssdk.core.retry.RetryMode;
5859
import software.amazon.awssdk.core.retry.RetryPolicy;
5960
import software.amazon.awssdk.http.SdkHttpClient;
@@ -457,12 +458,18 @@ private void configureRetryStrategy(SdkClientConfiguration.Builder config) {
457458
}
458459

459460
private RetryStrategy resolveAwsRetryStrategy(LazyValueSource config) {
461+
Boolean defaultNewRetries2026 = config.get(SdkClientOption.NEW_RETRIES_2026_DEFAULT);
462+
460463
RetryMode retryMode = RetryMode.resolver()
461464
.profileFile(config.get(SdkClientOption.PROFILE_FILE_SUPPLIER))
462465
.profileName(config.get(SdkClientOption.PROFILE_NAME))
463466
.defaultRetryMode(config.get(SdkClientOption.DEFAULT_RETRY_MODE))
467+
.defaultNewRetries2026(defaultNewRetries2026)
464468
.resolve();
465-
return AwsRetryStrategy.forRetryMode(retryMode);
469+
470+
NewRetries2026Resolver newRetries2026Resolver = new NewRetries2026Resolver().defaultNewRetries2026(defaultNewRetries2026);
471+
472+
return AwsRetryStrategy.forRetryMode(retryMode, newRetries2026Resolver.resolve());
466473
}
467474

468475
@Override

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

Lines changed: 42 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -63,17 +63,29 @@ public static RetryStrategy defaultRetryStrategy() {
6363
}
6464

6565
/**
66-
* Retrieve the appropriate retry strategy for the retry mode with AWS-specific conditions added.
66+
* Retrieve the appropriate retry strategy for the retry mode with AWS-specific conditions added. This is equivalent to
67+
* {@code forRetryMode(mode, false)}.
6768
*
6869
* @param mode The retry mode for which we want to create a retry strategy.
6970
* @return A retry strategy for the given retry mode.
7071
*/
7172
public static RetryStrategy forRetryMode(RetryMode mode) {
73+
return forRetryMode(mode, false);
74+
}
75+
76+
/**
77+
* Retrieve the appropriate retry strategy for the retry mode with AWS-specific conditions added.
78+
*
79+
* @param mode The retry mode for which we want to create a retry strategy.
80+
* @param newRetries2026Enabled Whether retries 2.1 behavior is enabled.
81+
* @return A retry strategy for the given retry mode.
82+
*/
83+
public static RetryStrategy forRetryMode(RetryMode mode, boolean newRetries2026Enabled) {
7284
switch (mode) {
7385
case STANDARD:
74-
return standardRetryStrategy();
86+
return standardRetryStrategy(newRetries2026Enabled);
7587
case ADAPTIVE_V2:
76-
return adaptiveRetryStrategy();
88+
return adaptiveRetryStrategy(newRetries2026Enabled);
7789
case LEGACY:
7890
return legacyRetryStrategy();
7991
case ADAPTIVE:
@@ -83,6 +95,7 @@ public static RetryStrategy forRetryMode(RetryMode mode) {
8395
}
8496
}
8597

98+
8699
/**
87100
* Update the provided {@link RetryStrategy} to add AWS-specific conditions.
88101
*
@@ -105,12 +118,23 @@ public static RetryStrategy doNotRetry() {
105118
}
106119

107120
/**
108-
* Returns a {@link StandardRetryStrategy} with AWS-specific conditions added.
121+
* Returns a {@link StandardRetryStrategy} with AWS-specific conditions added. This is equivalent to {@code
122+
* standardRetryStrategy(false)}.
109123
*
110124
* @return A {@link StandardRetryStrategy} with AWS-specific conditions added.
111125
*/
112126
public static StandardRetryStrategy standardRetryStrategy() {
113-
StandardRetryStrategy.Builder builder = SdkDefaultRetryStrategy.standardRetryStrategyBuilder();
127+
return standardRetryStrategy(false);
128+
}
129+
130+
/**
131+
* Returns a {@link StandardRetryStrategy} with AWS-specific conditions added.
132+
*
133+
* @param newRetries2026Enabled Whether retries 2.1 behavior is enabled.
134+
* @return A {@link StandardRetryStrategy} with AWS-specific conditions added.
135+
*/
136+
public static StandardRetryStrategy standardRetryStrategy(boolean newRetries2026Enabled) {
137+
StandardRetryStrategy.Builder builder = SdkDefaultRetryStrategy.standardRetryStrategyBuilder(newRetries2026Enabled);
114138
return configure(builder).build();
115139
}
116140

@@ -126,12 +150,23 @@ public static LegacyRetryStrategy legacyRetryStrategy() {
126150
}
127151

128152
/**
129-
* Returns an {@link AdaptiveRetryStrategy} with AWS-specific conditions added.
153+
* Returns an {@link AdaptiveRetryStrategy} with AWS-specific conditions added. This is equivalent to {@code
154+
* adaptiveRetryStrategy(false)}.
130155
*
131156
* @return An {@link AdaptiveRetryStrategy} with AWS-specific conditions added.
132157
*/
133158
public static AdaptiveRetryStrategy adaptiveRetryStrategy() {
134-
AdaptiveRetryStrategy.Builder builder = SdkDefaultRetryStrategy.adaptiveRetryStrategyBuilder();
159+
return adaptiveRetryStrategy(false);
160+
}
161+
162+
/**
163+
* Returns an {@link AdaptiveRetryStrategy} with AWS-specific conditions added.
164+
*
165+
* @param newRetries2026Enabled Whether retries 2.1 behavior is enabled.
166+
* @return An {@link AdaptiveRetryStrategy} with AWS-specific conditions added.
167+
*/
168+
public static AdaptiveRetryStrategy adaptiveRetryStrategy(boolean newRetries2026Enabled) {
169+
AdaptiveRetryStrategy.Builder builder = SdkDefaultRetryStrategy.adaptiveRetryStrategyBuilder(newRetries2026Enabled);
135170
return configure(builder)
136171
.build();
137172
}
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
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.client.builder;
17+
18+
import static org.assertj.core.api.Assertions.assertThat;
19+
import static org.mockito.Mockito.mock;
20+
import static software.amazon.awssdk.core.client.config.SdkClientOption.RETRY_STRATEGY;
21+
22+
import java.util.stream.Stream;
23+
import org.junit.jupiter.api.AfterAll;
24+
import org.junit.jupiter.api.BeforeAll;
25+
import org.junit.jupiter.api.BeforeEach;
26+
import org.junit.jupiter.params.ParameterizedTest;
27+
import org.junit.jupiter.params.provider.Arguments;
28+
import org.junit.jupiter.params.provider.MethodSource;
29+
import software.amazon.awssdk.core.SdkSystemSetting;
30+
import software.amazon.awssdk.core.client.config.SdkClientConfiguration;
31+
import software.amazon.awssdk.core.client.config.SdkClientOption;
32+
import software.amazon.awssdk.http.SdkHttpClient;
33+
import software.amazon.awssdk.http.async.SdkAsyncHttpClient;
34+
import software.amazon.awssdk.retries.LegacyRetryStrategy;
35+
import software.amazon.awssdk.retries.StandardRetryStrategy;
36+
import software.amazon.awssdk.testutils.EnvironmentVariableHelper;
37+
38+
public class InternalDefaultsTest {
39+
private static String newRetries2026Save;
40+
41+
@BeforeAll
42+
static void setup() {
43+
newRetries2026Save = System.getProperty(SdkSystemSetting.AWS_NEW_RETRIES_2026.property());
44+
}
45+
46+
@BeforeEach
47+
void methodSetup() {
48+
System.clearProperty(SdkSystemSetting.AWS_NEW_RETRIES_2026.property());
49+
}
50+
51+
@AfterAll
52+
static void teardown() {
53+
if (newRetries2026Save != null) {
54+
System.setProperty(SdkSystemSetting.AWS_NEW_RETRIES_2026.property(), newRetries2026Save);
55+
} else {
56+
System.clearProperty(SdkSystemSetting.AWS_NEW_RETRIES_2026.property());
57+
}
58+
}
59+
60+
@ParameterizedTest(name = "system prop = {0}, env var = {1}, default cfg = {2}, expected = {3}")
61+
@MethodSource("newRetries2026Settings")
62+
void buildClient_precedenceIsCorrect(String systemProperty, String environmentVariable, Boolean defaultConfig,
63+
Class<?> retryStrategyClass) {
64+
EnvironmentVariableHelper.run((env) -> {
65+
if (environmentVariable != null) {
66+
env.set(SdkSystemSetting.AWS_NEW_RETRIES_2026.environmentVariable(), environmentVariable);
67+
}
68+
69+
if (systemProperty != null) {
70+
System.setProperty(SdkSystemSetting.AWS_NEW_RETRIES_2026.property(), systemProperty);
71+
}
72+
73+
TestClient sync = new TestClientBuilder(true)
74+
.newRetries2026Default(defaultConfig)
75+
.buildClient();
76+
77+
TestClient async = new TestClientBuilder(false)
78+
.newRetries2026Default(defaultConfig)
79+
.buildClient();
80+
81+
assertThat(sync.clientConfiguration.option(RETRY_STRATEGY)).isInstanceOf(retryStrategyClass);
82+
assertThat(async.clientConfiguration.option(RETRY_STRATEGY)).isInstanceOf(retryStrategyClass);
83+
});
84+
}
85+
86+
// system property, environment variable, default config, expected retry strategy
87+
static Stream<Arguments> newRetries2026Settings() {
88+
return Stream.of(
89+
Arguments.of(null, null, null, LegacyRetryStrategy.class),
90+
91+
Arguments.of("true", null, null, StandardRetryStrategy.class),
92+
Arguments.of("false", null, null, LegacyRetryStrategy.class),
93+
Arguments.of(null, "true", null, StandardRetryStrategy.class),
94+
Arguments.of(null, "false", null, LegacyRetryStrategy.class),
95+
Arguments.of(null, null, true, StandardRetryStrategy.class),
96+
Arguments.of(null, null, false, LegacyRetryStrategy.class),
97+
98+
Arguments.of("true", null, false, StandardRetryStrategy.class),
99+
Arguments.of(null, "true", false, StandardRetryStrategy.class)
100+
);
101+
}
102+
103+
private static class TestClient {
104+
private final SdkClientConfiguration clientConfiguration;
105+
106+
public TestClient(SdkClientConfiguration clientConfiguration) {
107+
this.clientConfiguration = clientConfiguration;
108+
}
109+
}
110+
111+
private static class TestClientBuilder extends AwsDefaultClientBuilder<TestClientBuilder, TestClient> {
112+
private final boolean sync;
113+
private Boolean newRetries2026Default;
114+
115+
protected TestClientBuilder(boolean sync) {
116+
super(mock(SdkHttpClient.Builder.class), mock(SdkAsyncHttpClient.Builder.class), null);
117+
this.sync = sync;
118+
}
119+
120+
public TestClientBuilder newRetries2026Default(Boolean newRetries2026Default) {
121+
this.newRetries2026Default = newRetries2026Default;
122+
return this;
123+
}
124+
125+
@Override
126+
protected String serviceEndpointPrefix() {
127+
return "test-client";
128+
}
129+
130+
@Override
131+
protected String signingName() {
132+
return "test-client";
133+
}
134+
135+
@Override
136+
protected String serviceName() {
137+
return "test-client";
138+
}
139+
140+
@Override
141+
protected final SdkClientConfiguration mergeInternalDefaults(SdkClientConfiguration config) {
142+
return config.merge(c -> {
143+
c.option(SdkClientOption.NEW_RETRIES_2026_DEFAULT, newRetries2026Default);
144+
});
145+
}
146+
147+
@Override
148+
protected TestClient buildClient() {
149+
SdkClientConfiguration config;
150+
if (sync) {
151+
config = syncClientConfiguration();
152+
} else {
153+
config = asyncClientConfiguration();
154+
}
155+
156+
return new TestClient(config);
157+
}
158+
}
159+
}

0 commit comments

Comments
 (0)