Skip to content

Commit 587a95b

Browse files
authored
Expose thresholdSizeInBytes in AWS CRT-based S3 client (#4282)
1 parent 7697807 commit 587a95b

File tree

7 files changed

+72
-6
lines changed

7 files changed

+72
-6
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"type": "feature",
3+
"category": "Amazon S3",
4+
"contributor": "",
5+
"description": "Allow users to configure upload threshold size for AWS CRT-based S3 client via `S3CrtAsyncClientBuilder#thresholdInBytes`."
6+
}

services/s3/src/it/java/software/amazon/awssdk/services/s3/crt/S3CrtClientPutObjectIntegrationTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@
4747
public class S3CrtClientPutObjectIntegrationTest extends S3IntegrationTestBase {
4848
private static final String TEST_BUCKET = temporaryBucketName(S3CrtClientPutObjectIntegrationTest.class);
4949
private static final String TEST_KEY = "8mib_file.dat";
50-
private static final int OBJ_SIZE = 8 * 1024 * 1024;
50+
private static final int OBJ_SIZE = 10 * 1024 * 1024;
5151

5252
private static RandomTempFile testFile;
5353
private static S3AsyncClient s3Crt;

services/s3/src/main/java/software/amazon/awssdk/services/s3/S3CrtAsyncClientBuilder.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,26 @@ default S3CrtAsyncClientBuilder retryConfiguration(Consumer<S3CrtRetryConfigurat
234234
*/
235235
S3CrtAsyncClientBuilder crossRegionAccessEnabled(Boolean crossRegionAccessEnabled);
236236

237+
/**
238+
* Configure the size threshold, in bytes, for when to use multipart upload. Uploads/copies over this size will automatically
239+
* use a multipart upload strategy, while uploads/copies smaller than this threshold will use a single connection to
240+
* upload/copy the whole object.
241+
*
242+
* <p>
243+
* Multipart uploads are easier to recover from and also potentially faster than single part uploads, especially when the
244+
* upload parts can be uploaded in parallel. Because there are additional network API calls, small objects are still
245+
* recommended to use a single connection for the upload. See
246+
* <a href="https://docs.aws.amazon.com/AmazonS3/latest/userguide/mpuoverview.html">Uploading and copying objects using
247+
* multipart upload</a>.
248+
*
249+
* <p>
250+
* By default, it is the same as {@link #minimumPartSizeInBytes(Long)}.
251+
*
252+
* @param thresholdInBytes the value of the threshold to set.
253+
* @return an instance of this builder.
254+
*/
255+
S3CrtAsyncClientBuilder thresholdInBytes(Long thresholdInBytes);
256+
237257
@Override
238258
S3AsyncClient build();
239259
}

services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/crt/DefaultS3CrtAsyncClient.java

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,9 +70,10 @@ private DefaultS3CrtAsyncClient(DefaultS3CrtClientBuilder builder) {
7070
super(initializeS3AsyncClient(builder));
7171
long partSizeInBytes = builder.minimalPartSizeInBytes == null ? DEFAULT_PART_SIZE_IN_BYTES :
7272
builder.minimalPartSizeInBytes;
73+
long thresholdInBytes = builder.thresholdInBytes == null ? partSizeInBytes : builder.thresholdInBytes;
7374
this.copyObjectHelper = new CopyObjectHelper((S3AsyncClient) delegate(),
7475
partSizeInBytes,
75-
partSizeInBytes);
76+
thresholdInBytes);
7677
}
7778

7879
@Override
@@ -117,6 +118,7 @@ private static S3CrtAsyncHttpClient.Builder initializeS3CrtAsyncHttpClient(Defau
117118
Validate.isPositiveOrNull(builder.maxConcurrency, "maxConcurrency");
118119
Validate.isPositiveOrNull(builder.targetThroughputInGbps, "targetThroughputInGbps");
119120
Validate.isPositiveOrNull(builder.minimalPartSizeInBytes, "minimalPartSizeInBytes");
121+
Validate.isPositiveOrNull(builder.thresholdInBytes, "thresholdInBytes");
120122

121123
S3NativeClientConfiguration.Builder nativeClientBuilder =
122124
S3NativeClientConfiguration.builder()
@@ -128,7 +130,8 @@ private static S3CrtAsyncHttpClient.Builder initializeS3CrtAsyncHttpClient(Defau
128130
.endpointOverride(builder.endpointOverride)
129131
.credentialsProvider(builder.credentialsProvider)
130132
.readBufferSizeInBytes(builder.readBufferSizeInBytes)
131-
.httpConfiguration(builder.httpConfiguration);
133+
.httpConfiguration(builder.httpConfiguration)
134+
.thresholdInBytes(builder.thresholdInBytes);
132135

133136
if (builder.retryConfiguration != null) {
134137
nativeClientBuilder.standardRetryOptions(
@@ -156,6 +159,7 @@ public static final class DefaultS3CrtClientBuilder implements S3CrtAsyncClientB
156159
private List<ExecutionInterceptor> executionInterceptors;
157160
private S3CrtRetryConfiguration retryConfiguration;
158161
private boolean crossRegionAccessEnabled;
162+
private Long thresholdInBytes;
159163

160164
public AwsCredentialsProvider credentialsProvider() {
161165
return credentialsProvider;
@@ -276,6 +280,12 @@ public S3CrtAsyncClientBuilder crossRegionAccessEnabled(Boolean crossRegionAcces
276280
return this;
277281
}
278282

283+
@Override
284+
public S3CrtAsyncClientBuilder thresholdInBytes(Long thresholdInBytes) {
285+
this.thresholdInBytes = thresholdInBytes;
286+
return this;
287+
}
288+
279289
@Override
280290
public S3CrtAsyncClient build() {
281291
return new DefaultS3CrtAsyncClient(this);

services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/crt/S3CrtAsyncHttpClient.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ private S3CrtAsyncHttpClient(Builder builder) {
7575
.withCredentialsProvider(s3NativeClientConfiguration.credentialsProvider())
7676
.withClientBootstrap(s3NativeClientConfiguration.clientBootstrap())
7777
.withPartSize(s3NativeClientConfiguration.partSizeBytes())
78+
.withMultipartUploadThreshold(s3NativeClientConfiguration.thresholdInBytes())
7879
.withComputeContentMd5(false)
7980
.withMaxConnections(s3NativeClientConfiguration.maxConcurrency())
8081
.withThroughputTargetGbps(s3NativeClientConfiguration.targetThroughputInGbps())

services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/crt/S3NativeClientConfiguration.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ public class S3NativeClientConfiguration implements SdkAutoCloseable {
5151
private final CrtCredentialsProviderAdapter credentialProviderAdapter;
5252
private final CredentialsProvider credentialsProvider;
5353
private final long partSizeInBytes;
54+
private final long thresholdInBytes;
5455
private final double targetThroughputInGbps;
5556
private final int maxConcurrency;
5657
private final URI endpointOverride;
@@ -86,6 +87,8 @@ public S3NativeClientConfiguration(Builder builder) {
8687

8788
this.partSizeInBytes = builder.partSizeInBytes == null ? DEFAULT_PART_SIZE_IN_BYTES :
8889
builder.partSizeInBytes;
90+
this.thresholdInBytes = builder.thresholdInBytes == null ? this.partSizeInBytes :
91+
builder.thresholdInBytes;
8992
this.targetThroughputInGbps = builder.targetThroughputInGbps == null ?
9093
DEFAULT_TARGET_THROUGHPUT_IN_GBPS : builder.targetThroughputInGbps;
9194

@@ -144,6 +147,10 @@ public long partSizeBytes() {
144147
return partSizeInBytes;
145148
}
146149

150+
public long thresholdInBytes() {
151+
return thresholdInBytes;
152+
}
153+
147154
public double targetThroughputInGbps() {
148155
return targetThroughputInGbps;
149156
}
@@ -187,6 +194,7 @@ public static final class Builder {
187194

188195
private S3CrtHttpConfiguration httpConfiguration;
189196
private StandardRetryOptions standardRetryOptions;
197+
private Long thresholdInBytes;
190198

191199
private Builder() {
192200
}
@@ -247,5 +255,10 @@ public Builder standardRetryOptions(StandardRetryOptions standardRetryOptions) {
247255
this.standardRetryOptions = standardRetryOptions;
248256
return this;
249257
}
258+
259+
public Builder thresholdInBytes(Long thresholdInBytes) {
260+
this.thresholdInBytes = thresholdInBytes;
261+
return this;
262+
}
250263
}
251264
}

services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/crt/S3CrtAsyncHttpClientTest.java

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -316,9 +316,10 @@ void build_shouldPassThroughParameters() {
316316
S3NativeClientConfiguration.builder()
317317
.maxConcurrency(100)
318318
.signingRegion("us-west-2")
319-
.standardRetryOptions(
320-
new StandardRetryOptions()
321-
.withBackoffRetryOptions(new ExponentialBackoffRetryOptions().withMaxRetries(7)))
319+
.thresholdInBytes(1024L)
320+
.standardRetryOptions(
321+
new StandardRetryOptions()
322+
.withBackoffRetryOptions(new ExponentialBackoffRetryOptions().withMaxRetries(7)))
322323
.httpConfiguration(S3CrtHttpConfiguration.builder()
323324
.connectionTimeout(Duration.ofSeconds(1))
324325
.connectionHealthConfiguration(c -> c.minimumThroughputInBps(1024L)
@@ -330,6 +331,7 @@ void build_shouldPassThroughParameters() {
330331
(S3CrtAsyncHttpClient) S3CrtAsyncHttpClient.builder().s3ClientConfiguration(configuration).build();
331332
S3ClientOptions clientOptions = client.s3ClientOptions();
332333
assertThat(clientOptions.getConnectTimeoutMs()).isEqualTo(1000);
334+
assertThat(clientOptions.getMultiPartUploadThreshold()).isEqualTo(1024);
333335
assertThat(clientOptions.getStandardRetryOptions().getBackoffRetryOptions().getMaxRetries()).isEqualTo(7);
334336
assertThat(clientOptions.getMaxConnections()).isEqualTo(100);
335337
assertThat(clientOptions.getMonitoringOptions()).satisfies(options -> {
@@ -347,6 +349,20 @@ void build_shouldPassThroughParameters() {
347349
assertThat(clientOptions.getMaxConnections()).isEqualTo(100);
348350
}
349351

352+
@Test
353+
void build_partSizeConfigured_shouldApplyToThreshold() {
354+
long partSizeInBytes = 10L;
355+
S3NativeClientConfiguration configuration =
356+
S3NativeClientConfiguration.builder()
357+
.partSizeInBytes(partSizeInBytes)
358+
.build();
359+
S3CrtAsyncHttpClient client =
360+
(S3CrtAsyncHttpClient) S3CrtAsyncHttpClient.builder().s3ClientConfiguration(configuration).build();
361+
S3ClientOptions clientOptions = client.s3ClientOptions();
362+
assertThat(clientOptions.getPartSize()).isEqualTo(partSizeInBytes);
363+
assertThat(clientOptions.getMultiPartUploadThreshold()).isEqualTo(clientOptions.getPartSize());
364+
}
365+
350366
@Test
351367
void build_nullHttpConfiguration() {
352368
S3NativeClientConfiguration configuration =

0 commit comments

Comments
 (0)