Skip to content

Remove the use of S3NativeClient and integrate with S3Client #2976

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 9 commits into from
Jan 24, 2022

Conversation

zoewangg
Copy link
Contributor

@zoewangg zoewangg commented Jan 19, 2022

Context

S3NativeClient: an S3 SDK client provided in the aws-crt-java. It handles the full request lifecycle: marshalling, signing, sending HTTP requests, retrying, unmarshalling.

S3Client: HTTP abstraction specific to S3 provided in aws-crt-java. It handles signing, sending HTTP requests and retrying.

Description

This PR removes the use of S3NativeClient and integrates with S3Client in the transfer manager. The integration is implemented by wrapping S3Client with SdkAsyncHttpClient and passing the HTTP client to the java S3AsyncClient with certain java features disabled.

S3AsyncClient.builder()
             // Disable checksum, retry policy and signer because they are handled in crt
             .serviceConfiguration(S3Configuration.builder()
             .checksumValidationEnabled(false)
             .build())
             .overrideConfiguration(o -> o.putAdvancedOption(SdkAdvancedClientOption.SIGNER,
                                                                            new NoOpSigner())
                                                         .retryPolicy(RetryPolicy.none())
             .addExecutionInterceptor(new AttachHttpAttributesExecutionInterceptor()))
             .httpClient(s3CrtAsyncHttpClient)
             .build();

This should fix #2864

Below are the public API changes:

  • AsyncExecuteRequest now takes additional SDK HTTP execution attributes. This is required to pass credentials, region and other s3 settings to the S3 HTTP client.

Testing

Added unit tests.
Integ tests and stability tests passed

Screenshots (if appropriate)

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)

Checklist

  • I have read the CONTRIBUTING document
  • Local run of mvn install succeeds
  • My code follows the code style of this project
  • My change requires a change to the Javadoc documentation
  • I have updated the Javadoc documentation accordingly
  • I have read the README document
  • I have added tests to cover my changes
  • All new and existing tests passed
  • A short description of the change has been added to the CHANGELOG
  • My change is to implement 1.11 parity feature and I have updated LaunchChangelog

License

  • I confirm that this pull request can be released under the Apache 2 license

@zoewangg zoewangg requested a review from a team as a code owner January 19, 2022 00:47
@zoewangg zoewangg force-pushed the zoewang/tm-removeS3NativeClient branch from 0e48aac to 1e593ae Compare January 19, 2022 03:09
Copy link
Contributor

@dagnir dagnir left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:shipit:

.metricCollector(httpMetricCollector)
.build();
.metricCollector(httpMetricCollector);
if (context.executionAttributes().getAttribute(SDK_HTTP_EXECUTION_ATTRIBUTES) != null) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we add a variant of getAttribute() that returns an Optional, so we don't have to do multiple lookups? It's a hyper-nit in terms of performance, but it would also help readability in places like this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, it'd be nice to have another method returning optional but since overload is not possible, I'd be hesitant to introduce another method with a different name unless we have strong reasons.

Comment on lines +191 to +193
executeRequestBuilder.httpExecutionAttributes(
context.executionAttributes()
.getAttribute(SDK_HTTP_EXECUTION_ATTRIBUTES));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if we just copied all executionAttributes to the request, rather than selectively choosing which ones to copy? Are we worried that would leak abstractions that are not relevant to the request?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, most of the execution attributes are used in the SDK core and not relevant to the HTTP request.


private AsyncExecuteRequest(BuilderImpl builder) {
this.request = builder.request;
this.requestContentPublisher = builder.requestContentPublisher;
this.responseHandler = builder.responseHandler;
this.metricCollector = builder.metricCollector;
this.isFullDuplex = builder.isFullDuplex;
this.sdkHttpExecutionAttributes = builder.executionAttributesBuilder.build();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be referencing a builder or an already-built object?

Copy link
Contributor Author

@zoewangg zoewangg Jan 21, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you mean? Is there any benefit of referencing an already-built object?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems unusual to me that we're calling builder.executionAttributesBuilder.build() rather than just builder.executionAttributes.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, executionAttributes is immutable, so we have to keep a executionAttributesBuilder in the Builder inner class to allow users to provide a single attribute. We could add another executionAttributes field which will be built in the AsyncExecuteRequest build() method, but I feel it's a bit overkill.

*/
@SdkInternalApi
public final class RequestDataSupplierAdapter implements RequestDataSupplier {
public final class RequestDataSupplierAdapter implements HttpRequestBodyStream {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Also rename RequestDataSupplierAdapter?


@Override
public String clientName() {
return "s3crt";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this follow our normal naming convention? I see other names like NettyNio, AwsCommonRuntime, Apache, etc.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is used in the user agent header. Per the new standard, we should make it as short as possible and lower case

Comment on lines +249 to +251
// Add the rest of the Headers
sdkRequest.headers().forEach((key, value) -> value.stream().map(val -> new HttpHeader(key, val))
.forEach(crtHeaderList::add));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this not going to result in adding duplicates for host/content-length?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, host and content-length will only be added if they are not present in the sdkRequest headers

}
}

private static boolean isErrorResponse(int responseStatus) {
Copy link
Contributor

@Bennett-Lynch Bennett-Lynch Jan 21, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be checking crtCode or responseStatus? I would assume crtCode would be non-zero based, while responseStatus would be HTTP status code based?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isErrorResponse is more of if there is an error response from the service. Apparently, responseStatus == 0 means the HTTP request itself fails.


private void handleError(int crtCode, int responseStatus, byte[] errorPayload) {
if (isErrorResponse(responseStatus) && errorPayload != null) {
publisher.deliverData(ByteBuffer.wrap(errorPayload));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it safe to assume the byte[] won't be reused?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I don't think it will be reused by CRT

@sonarqubecloud
Copy link

SonarCloud Quality Gate failed.    Quality Gate failed

Bug A 0 Bugs
Vulnerability A 0 Vulnerabilities
Security Hotspot A 0 Security Hotspots
Code Smell A 21 Code Smells

73.4% 73.4% Coverage
0.0% 0.0% Duplication

@zoewangg zoewangg merged commit 65f68be into master Jan 24, 2022
@zoewangg zoewangg deleted the zoewang/tm-removeS3NativeClient branch January 24, 2022 21:48
zoewangg added a commit that referenced this pull request Jan 25, 2022
zoewangg added a commit that referenced this pull request Jan 26, 2022
* Revert "Revert "Remove the use of S3NativeClient and integrate with S3Client (#2976)""

This reverts commit cf0d833.

* Fix region issue
aws-sdk-java-automation added a commit that referenced this pull request Apr 12, 2024
…240fc58e8

Pull request: release <- staging/47dc0a16-ab58-44a9-9b5e-36a240fc58e8
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

S3 Transfer Manager doesn't support bucket names with dots (.).
3 participants