Skip to content

Updated retry policy behavior. #1681

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Mar 7, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changes/next-release/bugfix-AWSSDKforJavav2-11a7359.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"type": "bugfix",
"category": "AWS SDK for Java v2",
"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 changes: 5 additions & 0 deletions .changes/next-release/bugfix-AWSSDKforJavav2-373ea2a.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"type": "bugfix",
"category": "AWS SDK for Java v2",
"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 changes: 5 additions & 0 deletions .changes/next-release/feature-AWSSDKforJavav2-c758c22.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"type": "feature",
"category": "AWS SDK for Java v2",
"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 changes: 5 additions & 0 deletions .changes/next-release/feature-AWSSDKforJavav2-f7b0ca8.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"type": "feature",
"category": "AWS SDK for Java v2",
"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."
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
import com.squareup.javapoet.TypeName;
import com.squareup.javapoet.TypeSpec;
import com.squareup.javapoet.TypeVariableName;

import java.util.Collections;
import java.util.List;
import javax.lang.model.element.Modifier;
Expand Down Expand Up @@ -160,7 +159,7 @@ private MethodSpec mergeServiceDefaultsMethod() {
SdkClientOption.class, crc32FromCompressedDataEnabled);

if (StringUtils.isNotBlank(model.getCustomizationConfig().getCustomRetryPolicy())) {
builder.addCode(".option($T.RETRY_POLICY, $T.defaultPolicy())", SdkClientOption.class,
builder.addCode(".option($T.RETRY_POLICY, $T.defaultRetryPolicy())", SdkClientOption.class,
PoetUtils.classNameFromFqcn(model.getCustomizationConfig().getCustomRetryPolicy()));
}
builder.addCode(");");
Expand Down Expand Up @@ -198,14 +197,20 @@ private MethodSpec finalizeServiceConfigurationMethod() {
.endControlFlow();

builder.addCode("return config.toBuilder()\n" +
" .option($1T.EXECUTION_INTERCEPTORS, interceptors)\n" +
" .option($1T.ENDPOINT_DISCOVERY_ENABLED, endpointDiscoveryEnabled)\n" +
" .build();", SdkClientOption.class);
".option($T.ENDPOINT_DISCOVERY_ENABLED, endpointDiscoveryEnabled)\n",
SdkClientOption.class);
} else {
builder.addCode("return config.toBuilder()\n" +
" .option($T.EXECUTION_INTERCEPTORS, interceptors)\n" +
" .build();", SdkClientOption.class);
builder.addCode("return config.toBuilder()\n");
}

builder.addCode(".option($1T.EXECUTION_INTERCEPTORS, interceptors)", SdkClientOption.class);

if (StringUtils.isNotBlank(model.getCustomizationConfig().getCustomRetryPolicy())) {
builder.addCode(".option($1T.RETRY_POLICY, $2T.addRetryConditions(config.option($1T.RETRY_POLICY)))",
SdkClientOption.class,
PoetUtils.classNameFromFqcn(model.getCustomizationConfig().getCustomRetryPolicy()));
}
builder.addCode(".build();");

return builder.build();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@ public void baseClientBuilderClass() throws Exception {
validateGeneration(BaseClientBuilderClass::new, "test-client-builder-class.java");
}

@Test
public void baseQueryClientBuilderClass() throws Exception {
validateQueryGeneration(BaseClientBuilderClass::new, "test-query-client-builder-class.java");
}

@Test
public void syncClientBuilderInterface() throws Exception {
validateGeneration(SyncClientBuilderInterface::new, "test-sync-client-builder-interface.java");
Expand All @@ -61,4 +66,8 @@ public void asyncClientBuilderClass() throws Exception {
private void validateGeneration(Function<IntermediateModel, ClassSpec> generatorConstructor, String expectedClassName) {
assertThat(generatorConstructor.apply(ClientTestModels.jsonServiceModels()), generatesTo(expectedClassName));
}

private void validateQueryGeneration(Function<IntermediateModel, ClassSpec> generatorConstructor, String expectedClassName) {
assertThat(generatorConstructor.apply(ClientTestModels.queryServiceModels()), generatesTo(expectedClassName));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import java.util.List;
import software.amazon.MyServiceHttpConfig;
import software.amazon.MyServiceRetryPolicy;
import software.amazon.awssdk.annotations.Generated;
import software.amazon.awssdk.annotations.SdkInternalApi;
import software.amazon.awssdk.auth.signer.Aws4Signer;
Expand Down Expand Up @@ -33,8 +34,9 @@ protected final String serviceName() {

@Override
protected final SdkClientConfiguration mergeServiceDefaults(SdkClientConfiguration config) {
return config.merge(c -> c.option(SdkAdvancedClientOption.SIGNER, defaultSigner()).option(
SdkClientOption.CRC32_FROM_COMPRESSED_DATA_ENABLED, false));
return config.merge(c -> c.option(SdkAdvancedClientOption.SIGNER, defaultSigner())
.option(SdkClientOption.CRC32_FROM_COMPRESSED_DATA_ENABLED, false)
.option(SdkClientOption.RETRY_POLICY, MyServiceRetryPolicy.defaultRetryPolicy()));
}

@Override
Expand All @@ -43,7 +45,11 @@ protected final SdkClientConfiguration finalizeServiceConfiguration(SdkClientCon
List<ExecutionInterceptor> interceptors = interceptorFactory
.getInterceptors("software/amazon/awssdk/services/json/execution.interceptors");
interceptors = CollectionUtils.mergeLists(interceptors, config.option(SdkClientOption.EXECUTION_INTERCEPTORS));
return config.toBuilder().option(SdkClientOption.EXECUTION_INTERCEPTORS, interceptors).build();
return config
.toBuilder()
.option(SdkClientOption.EXECUTION_INTERCEPTORS, interceptors)
.option(SdkClientOption.RETRY_POLICY,
MyServiceRetryPolicy.addRetryConditions(config.option(SdkClientOption.RETRY_POLICY))).build();
}

private Signer defaultSigner() {
Expand All @@ -70,4 +76,3 @@ protected final AttributeMap serviceHttpConfig() {
return result;
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package software.amazon.awssdk.services.query;

import java.util.Collections;
import java.util.List;
import software.amazon.awssdk.annotations.Generated;
import software.amazon.awssdk.annotations.SdkInternalApi;
import software.amazon.awssdk.auth.signer.Aws4Signer;
import software.amazon.awssdk.awscore.client.builder.AwsDefaultClientBuilder;
import software.amazon.awssdk.core.client.config.SdkAdvancedClientOption;
import software.amazon.awssdk.core.client.config.SdkClientConfiguration;
import software.amazon.awssdk.core.client.config.SdkClientOption;
import software.amazon.awssdk.core.interceptor.ClasspathInterceptorChainFactory;
import software.amazon.awssdk.core.interceptor.ExecutionInterceptor;
import software.amazon.awssdk.core.signer.Signer;
import software.amazon.awssdk.protocols.query.interceptor.QueryParametersToBodyInterceptor;
import software.amazon.awssdk.utils.CollectionUtils;

/**
* Internal base class for {@link DefaultQueryClientBuilder} and {@link DefaultQueryAsyncClientBuilder}.
*/
@Generated("software.amazon.awssdk:codegen")
@SdkInternalApi
abstract class DefaultQueryBaseClientBuilder<B extends QueryBaseClientBuilder<B, C>, C> extends AwsDefaultClientBuilder<B, C> {
@Override
protected final String serviceEndpointPrefix() {
return "query-service";
}

@Override
protected final String serviceName() {
return "Query";
}

@Override
protected final SdkClientConfiguration mergeServiceDefaults(SdkClientConfiguration config) {
return config.merge(c -> c.option(SdkAdvancedClientOption.SIGNER, defaultSigner()).option(
SdkClientOption.CRC32_FROM_COMPRESSED_DATA_ENABLED, false));
}

@Override
protected final SdkClientConfiguration finalizeServiceConfiguration(SdkClientConfiguration config) {
ClasspathInterceptorChainFactory interceptorFactory = new ClasspathInterceptorChainFactory();
List<ExecutionInterceptor> interceptors = interceptorFactory
.getInterceptors("software/amazon/awssdk/services/query/execution.interceptors");
interceptors = CollectionUtils.mergeLists(interceptors, config.option(SdkClientOption.EXECUTION_INTERCEPTORS));
List<ExecutionInterceptor> protocolInterceptors = Collections.singletonList(new QueryParametersToBodyInterceptor());
interceptors = CollectionUtils.mergeLists(interceptors, protocolInterceptors);
return config.toBuilder().option(SdkClientOption.EXECUTION_INTERCEPTORS, interceptors).build();
}

private Signer defaultSigner() {
return Aws4Signer.create();
}

@Override
protected final String signingName() {
return "query-service";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"presignersFqcn": "software.amazon.awssdk.services.acm.presign.AcmClientPresigners",
"serviceSpecificHttpConfig": "software.amazon.MyServiceHttpConfig",
"serviceSpecificClientConfigClass": "ServiceConfiguration",
"customRetryPolicy": "software.amazon.MyServiceRetryPolicy",
"verifiedSimpleMethods" : ["paginatedOperationWithResultKey"],
"blacklistedSimpleMethods" : [
"eventStreamOperation"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import software.amazon.awssdk.auth.credentials.AwsCredentials;
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
import software.amazon.awssdk.utils.IoUtils;
import software.amazon.awssdk.utils.Lazy;
import software.amazon.awssdk.utils.SdkAutoCloseable;
import software.amazon.awssdk.utils.ToString;

Expand All @@ -29,11 +30,10 @@
*/
@SdkInternalApi
public class LazyAwsCredentialsProvider implements AwsCredentialsProvider, SdkAutoCloseable {
private final Supplier<AwsCredentialsProvider> delegateConstructor;
private volatile AwsCredentialsProvider delegate;
private final Lazy<AwsCredentialsProvider> delegate;

private LazyAwsCredentialsProvider(Supplier<AwsCredentialsProvider> delegateConstructor) {
this.delegateConstructor = delegateConstructor;
this.delegate = new Lazy<>(delegateConstructor);
}

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

@Override
public AwsCredentials resolveCredentials() {
if (delegate == null) {
synchronized (this) {
if (delegate == null) {
delegate = delegateConstructor.get();
}
}
}
return delegate.resolveCredentials();
return delegate.getValue().resolveCredentials();
}

@Override
Expand All @@ -60,7 +53,6 @@ public void close() {
@Override
public String toString() {
return ToString.builder("LazyAwsCredentialsProvider")
.add("delegateConstructor", delegateConstructor)
.add("delegate", delegate)
.build();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import software.amazon.awssdk.core.client.config.SdkClientConfiguration;
import software.amazon.awssdk.core.client.config.SdkClientOption;
import software.amazon.awssdk.core.interceptor.ExecutionInterceptor;
import software.amazon.awssdk.core.retry.RetryPolicy;
import software.amazon.awssdk.http.SdkHttpClient;
import software.amazon.awssdk.http.async.SdkAsyncHttpClient;
import software.amazon.awssdk.regions.Region;
Expand Down Expand Up @@ -113,10 +114,14 @@ protected AttributeMap serviceHttpConfig() {
@Override
protected final SdkClientConfiguration mergeChildDefaults(SdkClientConfiguration configuration) {
SdkClientConfiguration config = mergeServiceDefaults(configuration);

return config.merge(c -> c.option(AwsClientOption.AWS_REGION, resolveRegion(config))
.option(AwsAdvancedClientOption.ENABLE_DEFAULT_REGION_DETECTION, true)
.option(AwsClientOption.CREDENTIALS_PROVIDER, DefaultCredentialsProvider.create())
.option(SdkClientOption.RETRY_POLICY, AwsRetryPolicy.defaultRetryPolicy())
.option(SdkClientOption.RETRY_POLICY, AwsRetryPolicy.defaultRetryPolicy()
.toBuilder()
.additionalRetryConditionsAllowed(false)
.build())
.option(SdkAdvancedClientOption.DISABLE_HOST_PREFIX_INJECTION, false)
.option(AwsClientOption.SERVICE_SIGNING_NAME, signingName())
.option(SdkClientOption.SERVICE_NAME, serviceName())
Expand All @@ -137,6 +142,7 @@ protected final SdkClientConfiguration finalizeChildConfiguration(SdkClientConfi
.option(SdkClientOption.ENDPOINT, resolveEndpoint(configuration))
.option(SdkClientOption.EXECUTION_INTERCEPTORS, addAwsInterceptors(configuration))
.option(AwsClientOption.SIGNING_REGION, resolveSigningRegion(configuration))
.option(SdkClientOption.RETRY_POLICY, resolveRetryPolicy(configuration))
.build();
return finalizeServiceConfiguration(config);
}
Expand Down Expand Up @@ -186,6 +192,11 @@ private Region regionFromDefaultProvider(SdkClientConfiguration config) {
return DEFAULT_REGION_PROVIDER.getRegion();
}

private RetryPolicy resolveRetryPolicy(SdkClientConfiguration configuration) {
RetryPolicy policy = configuration.option(SdkClientOption.RETRY_POLICY);
return policy.additionalRetryConditionsAllowed() ? AwsRetryPolicy.addRetryConditions(policy) : policy;
}

@Override
public final BuilderT region(Region region) {
clientConfiguration.option(AwsClientOption.AWS_REGION, region);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import software.amazon.awssdk.annotations.SdkPublicApi;
import software.amazon.awssdk.awscore.internal.AwsErrorCode;
import software.amazon.awssdk.awscore.retry.conditions.RetryOnErrorCodeCondition;
import software.amazon.awssdk.core.retry.RetryMode;
import software.amazon.awssdk.core.retry.RetryPolicy;
import software.amazon.awssdk.core.retry.conditions.OrRetryCondition;
import software.amazon.awssdk.core.retry.conditions.RetryCondition;
Expand All @@ -31,12 +32,38 @@ public final class AwsRetryPolicy {
private AwsRetryPolicy() {
}

/**
* Retrieve the {@link RetryCondition#defaultRetryCondition()} with AWS-specific conditions added.
*/
public static RetryCondition defaultRetryCondition() {
return OrRetryCondition.create(RetryCondition.defaultRetryCondition(),
RetryOnErrorCodeCondition.create(AwsErrorCode.RETRYABLE_ERROR_CODES));
return OrRetryCondition.create(RetryCondition.defaultRetryCondition(), awsRetryCondition());
}

/**
* Retrieve the {@link RetryPolicy#defaultRetryPolicy()} with AWS-specific conditions added.
*/
public static RetryPolicy defaultRetryPolicy() {
return RetryPolicy.defaultRetryPolicy().toBuilder().retryCondition(defaultRetryCondition()).build();
return forRetryMode(RetryMode.defaultRetryMode());
}

/**
* Retrieve the {@link RetryPolicy#defaultRetryPolicy()} with AWS-specific conditions added. This uses the specified
* {@link RetryMode} when constructing the {@link RetryPolicy}.
*/
public static RetryPolicy forRetryMode(RetryMode retryMode) {
return addRetryConditions(RetryPolicy.forRetryMode(retryMode));
}

/**
* Update the provided {@link RetryPolicy} to add AWS-specific conditions.
*/
public static RetryPolicy addRetryConditions(RetryPolicy condition) {
return condition.toBuilder()
.retryCondition(OrRetryCondition.create(condition.retryCondition(), awsRetryCondition()))
.build();
}

private static RetryOnErrorCodeCondition awsRetryCondition() {
return RetryOnErrorCodeCondition.create(AwsErrorCode.RETRYABLE_ERROR_CODES);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@
import java.util.concurrent.Executors;
import software.amazon.awssdk.core.client.config.SdkAdvancedAsyncClientOption;
import software.amazon.awssdk.core.client.config.SdkAdvancedClientOption;
import software.amazon.awssdk.core.client.config.SdkClientOption;
import software.amazon.awssdk.core.client.config.SdkClientConfiguration;
import software.amazon.awssdk.core.client.config.SdkClientOption;
import software.amazon.awssdk.core.internal.http.AmazonSyncHttpClient;
import software.amazon.awssdk.core.internal.http.loader.DefaultSdkHttpClientBuilder;
import software.amazon.awssdk.core.retry.RetryPolicy;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,10 @@ public static Aggregator aggregator() {
* Get the default profile file, using the credentials file from "~/.aws/credentials", the config file from "~/.aws/config"
* and the "default" profile. This default behavior can be customized using the
* {@link ProfileFileSystemSetting#AWS_SHARED_CREDENTIALS_FILE}, {@link ProfileFileSystemSetting#AWS_CONFIG_FILE} and
* {@link ProfileFileSystemSetting#AWS_PROFILE} settings or by specifying a different profile file and profile name
* {@link ProfileFileSystemSetting#AWS_PROFILE} settings or by specifying a different profile file and profile name.
*
* <p>
* The file is read each time this method is invoked.
*/
public static ProfileFile defaultProfileFile() {
return ProfileFile.aggregator()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,5 +93,11 @@ public final class ProfileProperty {
*/
public static final String S3_US_EAST_1_REGIONAL_ENDPOINT = "s3_us_east_1_regional_endpoint";

/**
* The "retry mode" to be used for clients created using the currently-configured profile. Values supported by all SDKs are
* "legacy" and "standard". See the {@code RetryMode} class JavaDoc for more information.
*/
public static final String RETRY_MODE = "retry_mode";

private ProfileProperty() {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
*/
@SdkProtectedApi
public final class AwsProfileRegionProvider implements AwsRegionProvider {

private final String profileName = ProfileFileSystemSetting.AWS_PROFILE.getStringValueOrThrow();

@Override
Expand Down
Loading