Skip to content
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

Mulitpart download workaround #4945

Merged
merged 17 commits into from
Feb 23, 2024
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
250dce3
Make Transfer Manager work by default with MultipartS3AsyncClient.
L-Applin Feb 20, 2024
da1da9e
changelog
L-Applin Feb 20, 2024
543598d
Merge branch 'master' into olapplin/mulitpart-download-workaround
L-Applin Feb 20, 2024
f09adda
Merge branch 'master' into olapplin/mulitpart-download-workaround
L-Applin Feb 20, 2024
45af11f
Merge branch 'master' into olapplin/mulitpart-download-workaround
L-Applin Feb 20, 2024
0dedeec
TODO formatting
L-Applin Feb 21, 2024
0ae56d0
Merge remote-tracking branch 'origin/olapplin/mulitpart-download-work…
L-Applin Feb 21, 2024
3bf0b98
Merge branch 'master' into olapplin/mulitpart-download-workaround
L-Applin Feb 21, 2024
539b9cc
update changelog and rename future variables
L-Applin Feb 21, 2024
c97cddf
Merge remote-tracking branch 'origin/olapplin/mulitpart-download-work…
L-Applin Feb 21, 2024
fe48033
Merge branch 'master' into olapplin/mulitpart-download-workaround
L-Applin Feb 22, 2024
c98178a
testing errors with InputStream and Publisher
L-Applin Feb 22, 2024
a1bcb82
Merge remote-tracking branch 'origin/olapplin/mulitpart-download-work…
L-Applin Feb 22, 2024
23d16e1
Merge branch 'master' into olapplin/mulitpart-download-workaround
L-Applin Feb 22, 2024
41bac26
Merge branch 'master' into olapplin/mulitpart-download-workaround
L-Applin Feb 22, 2024
3e1e26c
Merge branch 'master' into olapplin/mulitpart-download-workaround
L-Applin Feb 22, 2024
f6c7e87
Merge branch 'master' into olapplin/mulitpart-download-workaround
L-Applin Feb 23, 2024
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-787c575.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"type": "feature",
"category": "AWS SDK for Java v2",
L-Applin marked this conversation as resolved.
Show resolved Hide resolved
"contributor": "",
"description": "Make Transfer Manager work by default with MultipartS3AsyncClient."
L-Applin marked this conversation as resolved.
Show resolved Hide resolved
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import software.amazon.awssdk.core.exception.SdkClientException;
import software.amazon.awssdk.core.exception.SdkException;
import software.amazon.awssdk.core.internal.async.FileAsyncRequestBody;
import software.amazon.awssdk.services.s3.DelegatingS3AsyncClient;
import software.amazon.awssdk.services.s3.S3AsyncClient;
import software.amazon.awssdk.services.s3.internal.multipart.MultipartS3AsyncClient;
import software.amazon.awssdk.services.s3.internal.resource.S3AccessPointResource;
Expand Down Expand Up @@ -298,8 +299,7 @@ public <ResultT> Download<ResultT> download(DownloadRequest<ResultT> downloadReq
try {
assertNotUnsupportedArn(downloadRequest.getObjectRequest().bucket(), "download");

CompletableFuture<ResultT> crtFuture =
s3AsyncClient.getObject(downloadRequest.getObjectRequest(), responseTransformer);
CompletableFuture<ResultT> crtFuture = doGetObject(downloadRequest.getObjectRequest(), responseTransformer);
L-Applin marked this conversation as resolved.
Show resolved Hide resolved

// Forward download cancellation to CRT future
CompletableFutureUtils.forwardExceptionTo(returnFuture, crtFuture);
Expand Down Expand Up @@ -341,9 +341,7 @@ private TransferProgressUpdater doDownloadFile(

assertNotUnsupportedArn(downloadRequest.getObjectRequest().bucket(), "download");

CompletableFuture<GetObjectResponse> crtFuture =
s3AsyncClient.getObject(downloadRequest.getObjectRequest(),
responseTransformer);
CompletableFuture<GetObjectResponse> crtFuture = doGetObject(downloadRequest.getObjectRequest(), responseTransformer);

// Forward download cancellation to CRT future
CompletableFutureUtils.forwardExceptionTo(returnFuture, crtFuture);
Expand Down Expand Up @@ -511,4 +509,14 @@ private static boolean isMrapArn(Arn arn) {

return !s3EndpointResource.region().isPresent();
}

// TODO remove once MultipartS3AsyncClient is complete
private <ResultT> CompletableFuture<ResultT> doGetObject(
GetObjectRequest getObjectRequest, AsyncResponseTransformer<GetObjectResponse, ResultT> asyncResponseTransformer) {
S3AsyncClient clientToUse = s3AsyncClient;
if (s3AsyncClient instanceof MultipartS3AsyncClient) {
clientToUse = (S3AsyncClient) ((DelegatingS3AsyncClient) s3AsyncClient).delegate();
}
return clientToUse.getObject(getObjectRequest, asyncResponseTransformer);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import software.amazon.awssdk.core.internal.util.ClassLoaderHelper;
import software.amazon.awssdk.services.s3.S3AsyncClient;
import software.amazon.awssdk.services.s3.internal.crt.S3CrtAsyncClient;
import software.amazon.awssdk.services.s3.internal.multipart.MultipartS3AsyncClient;
import software.amazon.awssdk.transfer.s3.S3TransferManager;
import software.amazon.awssdk.utils.Logger;

Expand Down Expand Up @@ -56,6 +57,10 @@ public static S3TransferManager createTransferManager(DefaultBuilder tmBuilder)
log.warn(() -> "The provided DefaultS3AsyncClient is not an instance of S3CrtAsyncClient, and thus multipart"
+ " upload/download feature is not enabled and resumable file upload is not supported. To benefit "
+ "from maximum throughput, consider using S3AsyncClient.crtBuilder().build() instead.");
} else if (s3AsyncClient instanceof MultipartS3AsyncClient) {
log.warn(() -> "The provided S3AsyncClient is an instance of MultipartS3AsyncClient, and thus multipart"
L-Applin marked this conversation as resolved.
Show resolved Hide resolved
+ " download feature is not enabled. To benefit from all features, "
+ "consider using S3AsyncClient.crtBuilder().build() instead.");
} else {
log.debug(() -> "The provided S3AsyncClient is not an instance of S3CrtAsyncClient, and thus multipart"
+ " upload/download feature may not be enabled and resumable file upload may not be supported.");
Expand All @@ -68,7 +73,7 @@ private static Supplier<S3AsyncClient> defaultS3AsyncClient() {
if (crtInClasspath()) {
return S3AsyncClient::crtCreate;
}
return S3AsyncClient::create;
return S3AsyncClient.builder().multipartEnabled(true)::build;
}

private static boolean crtInClasspath() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* 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.transfer.s3.internal;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import java.nio.file.Paths;
import java.util.concurrent.CompletableFuture;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import software.amazon.awssdk.core.async.AsyncResponseTransformer;
import software.amazon.awssdk.services.s3.S3AsyncClient;
import software.amazon.awssdk.services.s3.internal.multipart.MultipartS3AsyncClient;
import software.amazon.awssdk.services.s3.model.GetObjectRequest;
import software.amazon.awssdk.services.s3.model.GetObjectResponse;
import software.amazon.awssdk.services.s3.multipart.MultipartConfiguration;
import software.amazon.awssdk.transfer.s3.S3TransferManager;
import software.amazon.awssdk.transfer.s3.model.CompletedFileDownload;

class MultipartDownloadJavaBasedTest {
private S3AsyncClient mockDelegate;
private MultipartS3AsyncClient s3Multi;
private S3TransferManager tm;
private UploadDirectoryHelper uploadDirectoryHelper;
private DownloadDirectoryHelper downloadDirectoryHelper;
private TransferManagerConfiguration configuration;

@BeforeEach
public void methodSetup() {
mockDelegate = mock(S3AsyncClient.class);
s3Multi = MultipartS3AsyncClient.create(mockDelegate, MultipartConfiguration.builder().build());
uploadDirectoryHelper = mock(UploadDirectoryHelper.class);
configuration = mock(TransferManagerConfiguration.class);
downloadDirectoryHelper = mock(DownloadDirectoryHelper.class);
tm = new GenericS3TransferManager(s3Multi, uploadDirectoryHelper, configuration, downloadDirectoryHelper);
}

@Test
void usingMultipartDownload_shouldNotThrowException() {
GetObjectResponse response = GetObjectResponse.builder().build();
when(mockDelegate.getObject(any(GetObjectRequest.class), any(AsyncResponseTransformer.class)))
.thenReturn(CompletableFuture.completedFuture(response));

CompletedFileDownload completedFileDownload = tm.downloadFile(d -> d.getObjectRequest(g -> g.bucket("bucket")
.key("key"))
.destination(Paths.get(".")))
.completionFuture()
.join();
assertThat(completedFileDownload.response()).isEqualTo(response);
}
}
Loading