Skip to content

Expose numRetries to configure maxRetries on error in the S3CrtClient Interface #3885

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 7 commits into from
Apr 7, 2023
Merged
Show file tree
Hide file tree
Changes from 4 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
6 changes: 6 additions & 0 deletions .changes/next-release/feature-AWSSDKforJavav2-ddc21df.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"type": "feature",
"category": "AWS SDK for Java v2",
"contributor": "",
"description": "Expose StandardRetryOptions of S3ClientOptions in the S3CrtClient Interface"
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.crt.S3CrtHttpConfiguration;
import software.amazon.awssdk.services.s3.crt.S3CrtRetryConfiguration;
import software.amazon.awssdk.services.s3.model.GetObjectRequest;
import software.amazon.awssdk.services.s3.model.PutObjectRequest;
import software.amazon.awssdk.utils.Validate;
Expand Down Expand Up @@ -161,6 +162,14 @@ public interface S3CrtAsyncClientBuilder extends SdkBuilder<S3CrtAsyncClientBuil
*/
S3CrtAsyncClientBuilder httpConfiguration(S3CrtHttpConfiguration configuration);

/**
* Sets the Retry configuration to use for this client.
*
* @param retryConfiguration The retry configurations to be used.
* @return The builder of the method chaining.
*/
S3CrtAsyncClientBuilder retryConfiguration(S3CrtRetryConfiguration retryConfiguration);

/**
* A convenience method that creates an instance of the {@link S3CrtHttpConfiguration} builder, avoiding the
* need to create one manually via {@link S3CrtHttpConfiguration#builder()}.
Expand All @@ -177,6 +186,25 @@ default S3CrtAsyncClientBuilder httpConfiguration(Consumer<S3CrtHttpConfiguratio
}


/**
* A convenience method that creates an instance of the {@link S3CrtRetryConfiguration} builder, avoiding the
* need to create one manually via {@link S3CrtRetryConfiguration#builder()}.
*
* @param retryConfigurationBuilder The retry config builder to use
* @return The builder of the method chaining.
* @see #retryConfiguration(S3CrtRetryConfiguration)
*/
default S3CrtAsyncClientBuilder retryConfiguration(Consumer<S3CrtRetryConfiguration.Builder> retryConfigurationBuilder) {
Validate.paramNotNull(retryConfigurationBuilder, "retryConfigurationBuilder");
return retryConfiguration(S3CrtRetryConfiguration.builder()
.applyMutation(retryConfigurationBuilder)
.build());
}





@Override
S3AsyncClient build();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/

package software.amazon.awssdk.services.s3.crt;

import java.util.Objects;
import software.amazon.awssdk.annotations.Immutable;
import software.amazon.awssdk.annotations.SdkPublicApi;
import software.amazon.awssdk.annotations.ThreadSafe;
import software.amazon.awssdk.services.s3.S3CrtAsyncClientBuilder;
import software.amazon.awssdk.utils.Validate;
import software.amazon.awssdk.utils.builder.CopyableBuilder;
import software.amazon.awssdk.utils.builder.ToCopyableBuilder;

/**
* Retry option configuration for AWS CRT-based S3 client.
*
* @see S3CrtAsyncClientBuilder#retryConfiguration
*/
@SdkPublicApi
@Immutable
@ThreadSafe
public final class S3CrtRetryConfiguration implements ToCopyableBuilder<S3CrtRetryConfiguration.Builder,
S3CrtRetryConfiguration> {
private final Integer numRetries;

private S3CrtRetryConfiguration(DefaultBuilder builder) {
Validate.notNull(builder.numRetries, "numRetries");
this.numRetries = builder.numRetries;
}

/**
* Creates a default builder for {@link S3CrtRetryConfiguration}.
*/
public static Builder builder() {
return new S3CrtRetryConfiguration.DefaultBuilder();
}

/**
* Retrieve the {@link S3CrtRetryConfiguration.Builder#numRetries(Integer)} configured on the builder.
*/
public Integer numRetries() {
return numRetries;
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
S3CrtRetryConfiguration that = (S3CrtRetryConfiguration) o;
return Objects.equals(numRetries, that.numRetries);
}

@Override
public int hashCode() {
return numRetries != null ? numRetries.hashCode() : 0;
}

@Override
public Builder toBuilder() {
return new S3CrtRetryConfiguration.DefaultBuilder(this);
}

public interface Builder extends CopyableBuilder<S3CrtRetryConfiguration.Builder, S3CrtRetryConfiguration> {

/**
* Configure the maximum number of times that a single request should be retried.
* @param numRetries
* @return The builder of the method chaining.
*/
Builder numRetries(Integer numRetries);

}

private static final class DefaultBuilder implements Builder {
private Integer numRetries;

private DefaultBuilder() {
}

private DefaultBuilder(S3CrtRetryConfiguration crtRetryConfiguration) {
this.numRetries = crtRetryConfiguration.numRetries;
}

@Override
public Builder numRetries(Integer numRetries) {
this.numRetries = numRetries;
return this;
}

@Override
public S3CrtRetryConfiguration build() {
return new S3CrtRetryConfiguration(this);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,16 @@
import software.amazon.awssdk.core.internal.util.ClassLoaderHelper;
import software.amazon.awssdk.core.retry.RetryPolicy;
import software.amazon.awssdk.core.signer.NoOpSigner;
import software.amazon.awssdk.crt.io.ExponentialBackoffRetryOptions;
import software.amazon.awssdk.crt.io.StandardRetryOptions;
import software.amazon.awssdk.http.SdkHttpExecutionAttributes;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.DelegatingS3AsyncClient;
import software.amazon.awssdk.services.s3.S3AsyncClient;
import software.amazon.awssdk.services.s3.S3Configuration;
import software.amazon.awssdk.services.s3.S3CrtAsyncClientBuilder;
import software.amazon.awssdk.services.s3.crt.S3CrtHttpConfiguration;
import software.amazon.awssdk.services.s3.crt.S3CrtRetryConfiguration;
import software.amazon.awssdk.services.s3.model.CopyObjectRequest;
import software.amazon.awssdk.services.s3.model.CopyObjectResponse;
import software.amazon.awssdk.services.s3.model.GetObjectRequest;
Expand Down Expand Up @@ -96,7 +99,7 @@ private static S3CrtAsyncHttpClient.Builder initializeS3CrtAsyncHttpClient(Defau
Validate.isPositiveOrNull(builder.targetThroughputInGbps, "targetThroughputInGbps");
Validate.isPositiveOrNull(builder.minimalPartSizeInBytes, "minimalPartSizeInBytes");

S3NativeClientConfiguration s3NativeClientConfiguration =
S3NativeClientConfiguration.Builder nativeClientBuilder =
S3NativeClientConfiguration.builder()
.checksumValidationEnabled(builder.checksumValidationEnabled)
.targetThroughputInGbps(builder.targetThroughputInGbps)
Expand All @@ -106,11 +109,16 @@ private static S3CrtAsyncHttpClient.Builder initializeS3CrtAsyncHttpClient(Defau
.endpointOverride(builder.endpointOverride)
.credentialsProvider(builder.credentialsProvider)
.readBufferSizeInBytes(builder.readBufferSizeInBytes)
.httpConfiguration(builder.httpConfiguration)
.build();
.httpConfiguration(builder.httpConfiguration);

if (builder.retryConfiguration != null) {
nativeClientBuilder.standardRetryOptions(
new StandardRetryOptions()
.withBackoffRetryOptions(new ExponentialBackoffRetryOptions()
.withMaxRetries(builder.retryConfiguration.numRetries())));
}
return S3CrtAsyncHttpClient.builder()
.s3ClientConfiguration(s3NativeClientConfiguration);
.s3ClientConfiguration(nativeClientBuilder.build());
}

public static final class DefaultS3CrtClientBuilder implements S3CrtAsyncClientBuilder {
Expand All @@ -123,6 +131,7 @@ public static final class DefaultS3CrtClientBuilder implements S3CrtAsyncClientB
private URI endpointOverride;
private Boolean checksumValidationEnabled;
private S3CrtHttpConfiguration httpConfiguration;
private S3CrtRetryConfiguration retryConfiguration;

public AwsCredentialsProvider credentialsProvider() {
return credentialsProvider;
Expand Down Expand Up @@ -206,6 +215,12 @@ public S3CrtAsyncClientBuilder httpConfiguration(S3CrtHttpConfiguration configur
return this;
}

@Override
public S3CrtAsyncClientBuilder retryConfiguration(S3CrtRetryConfiguration retryConfiguration) {
this.retryConfiguration = retryConfiguration;
return this;
}

@Override
public S3CrtAsyncClient build() {
return new DefaultS3CrtAsyncClient(this);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,9 @@ private S3CrtAsyncHttpClient(Builder builder) {
.withInitialReadWindowSize(initialWindowSize)
.withReadBackpressureEnabled(true);

if (s3NativeClientConfiguration.standardRetryOptions() != null) {
this.s3ClientOptions.withStandardRetryOptions(s3NativeClientConfiguration.standardRetryOptions());
}
Optional.ofNullable(s3NativeClientConfiguration.proxyOptions()).ifPresent(s3ClientOptions::withProxyOptions);
Optional.ofNullable(s3NativeClientConfiguration.connectionTimeout())
.map(Duration::toMillis)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import software.amazon.awssdk.crt.http.HttpMonitoringOptions;
import software.amazon.awssdk.crt.http.HttpProxyOptions;
import software.amazon.awssdk.crt.io.ClientBootstrap;
import software.amazon.awssdk.crt.io.StandardRetryOptions;
import software.amazon.awssdk.crt.io.TlsCipherPreference;
import software.amazon.awssdk.crt.io.TlsContext;
import software.amazon.awssdk.crt.io.TlsContextOptions;
Expand All @@ -43,6 +44,7 @@ public class S3NativeClientConfiguration implements SdkAutoCloseable {
private static final long DEFAULT_TARGET_THROUGHPUT_IN_GBPS = 10;

private final String signingRegion;
private final StandardRetryOptions standardRetryOptions;
private final ClientBootstrap clientBootstrap;
private final CrtCredentialsProviderAdapter credentialProviderAdapter;
private final CredentialsProvider credentialsProvider;
Expand Down Expand Up @@ -97,6 +99,7 @@ public S3NativeClientConfiguration(Builder builder) {
this.connectionTimeout = null;
this.httpMonitoringOptions = null;
}
this.standardRetryOptions = builder.standardRetryOptions;
}

public HttpMonitoringOptions httpMonitoringOptions() {
Expand Down Expand Up @@ -140,6 +143,10 @@ public int maxConcurrency() {
return maxConcurrency;
}

public StandardRetryOptions standardRetryOptions() {
return standardRetryOptions;
}

public URI endpointOverride() {
return endpointOverride;
}
Expand Down Expand Up @@ -169,6 +176,7 @@ public static final class Builder {
private URI endpointOverride;
private Boolean checksumValidationEnabled;
private S3CrtHttpConfiguration httpConfiguration;
private StandardRetryOptions standardRetryOptions;

private Builder() {
}
Expand Down Expand Up @@ -224,5 +232,10 @@ public Builder httpConfiguration(S3CrtHttpConfiguration httpConfiguration) {
this.httpConfiguration = httpConfiguration;
return this;
}

public Builder standardRetryOptions(StandardRetryOptions standardRetryOptions) {
this.standardRetryOptions = standardRetryOptions;
return this;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/

package software.amazon.awssdk.services.s3.crt;

import nl.jqno.equalsverifier.EqualsVerifier;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
import static org.assertj.core.api.Assertions.assertThatNullPointerException;


public class S3CrtRetryConfigurationTest {

@Test
void equalsHashcode() {
EqualsVerifier.forClass(S3CrtRetryConfiguration.class)
.withRedefinedSuperclass()
.verify();
}

@Test
void retryConfigurationWithNoMaxRetriesDefined(){
assertThatNullPointerException().isThrownBy(() ->S3CrtRetryConfiguration.builder().build())
.withMessage("numRetries");
}



}
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@
import org.mockito.Mockito;
import software.amazon.awssdk.core.interceptor.trait.HttpChecksum;
import software.amazon.awssdk.crt.http.HttpRequest;
import software.amazon.awssdk.crt.io.ExponentialBackoffRetryOptions;
import software.amazon.awssdk.crt.io.StandardRetryOptions;
import software.amazon.awssdk.crt.s3.ChecksumAlgorithm;
import software.amazon.awssdk.crt.s3.S3Client;
import software.amazon.awssdk.crt.s3.S3ClientOptions;
Expand Down Expand Up @@ -314,6 +316,9 @@ void build_shouldPassThroughParameters() {
S3NativeClientConfiguration.builder()
.maxConcurrency(100)
.signingRegion("us-west-2")
.standardRetryOptions(
new StandardRetryOptions()
.withBackoffRetryOptions(new ExponentialBackoffRetryOptions().withMaxRetries(7)))
.httpConfiguration(S3CrtHttpConfiguration.builder()
.connectionTimeout(Duration.ofSeconds(1))
.connectionHealthConfiguration(c -> c.minimumThroughputInBps(1024L)
Expand All @@ -325,6 +330,7 @@ void build_shouldPassThroughParameters() {
(S3CrtAsyncHttpClient) S3CrtAsyncHttpClient.builder().s3ClientConfiguration(configuration).build();
S3ClientOptions clientOptions = client.s3ClientOptions();
assertThat(clientOptions.getConnectTimeoutMs()).isEqualTo(1000);
assertThat(clientOptions.getStandardRetryOptions().getBackoffRetryOptions().getMaxRetries()).isEqualTo(7);
assertThat(clientOptions.getMaxConnections()).isEqualTo(100);
assertThat(clientOptions.getMonitoringOptions()).satisfies(options -> {
assertThat(options.getMinThroughputBytesPerSecond()).isEqualTo(1024);
Expand Down