Skip to content

Upgrade to AWS Java SDK v2 #6165

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 33 commits into from
Jul 6, 2025
Merged

Upgrade to AWS Java SDK v2 #6165

merged 33 commits into from
Jul 6, 2025

Conversation

jorgee
Copy link
Contributor

@jorgee jorgee commented Jun 5, 2025

This PR contains the changes to port the Amazon plugin to AWS SDK version 2. Find below the most relevant changes:

  • S3 Global Region flag: In v1, it was activated with S3Client.withForceGlobalBucketAcessEnabled(flag). In v2, it set the following flags S3Client.Builder.crossRegionAccessEnabled(flag) and S3Configuration.multiRegionAccessEnabled(flag).
  • Two S3Clients are created: the async client is used for operations performed through the S3TransferManager, and the sync client is used for other actions.

- AmazonS3Client.getS3AccountOwner() is not available in SDK v2. It was providing an ID used for checking the file access. In V2, the only way to retrieve the same ID is from a bucket owned by the user. To do it we need to list the buckets and get the owner field in the GetBucketACLResponse. If it is not possible to retrieve the ID because the user does not own any bucket, we perform the following fallback. In the case of READ access, it tries to retrieve the head of the object, It will fail if there isn't read access. In the case of writting, a warning is printed. It is the same as AWS NIO is doing to check the file access.

  • The setEndpoint and setRegion methods in the S3Client wrapper are removed as it is not available in the v2 clients. They were only used in tests.

  • CannedAccessControlList is split in two classes one for objects and another for buckets. In most of the code it has been substituted by ObjectCannedACL.

  • ContentType and ContentLength are part of the request instead of the ObjectMetadata, and they can be obtained invoking the S3client.headObject method in the SDK v2

  • S3ClientConfiguration doesn't exist in SDK v2. Two new classes have been created to emulate the same behaviour. They convert the properties to the SDK v2 sync and async client configurations.

  • SsoCredentialsProviderV1 class is not needed anymore as SDK v2 already manages the SSO credentials. The custom provider chain created in the S3FileSystemProvider.getCredetialsProvider0 to include the SsoCredentialsProviderV1 ihas been replace by the DefaultCredentialProvider in v2.

  • Credentials and config are automatically merged by SDK v2. No option for NXF_DISABLE_AWS_CONFIG_MERGE.

  • In V2, clients and requests are immutable and must be generated with a builder class. Some helper methods have been modified to pass builder classes instead of requests, such as makeJobDefRequest, configJobRefRequest, addVolumeMountsToContainer, etc.

  • S3 Parallel Download was deprecated and S3CopyStream was not used. They have been removed.

  • In v1, the upload directory was performed by walking through the different directory files and uploading them one by one. In v2, it has been substituted by the uploadDirectory method in the SDK.

jorgee added 5 commits June 5, 2025 12:38
Signed-off-by: jorgee <[email protected]>
Signed-off-by: jorgee <[email protected]>
Signed-off-by: jorgee <[email protected]>
Signed-off-by: jorgee <[email protected]>
Copy link

netlify bot commented Jun 5, 2025

Deploy Preview for nextflow-docs-staging canceled.

Name Link
🔨 Latest commit fd86ab4
🔍 Latest deploy log https://app.netlify.com/projects/nextflow-docs-staging/deploys/686a33cecd232c0008122e95

@bentsherman bentsherman mentioned this pull request Jun 16, 2025
@bentsherman bentsherman changed the title AWS SDK V2 Porting (without AWS NIO SPI) Upgrade to AWS Java SDK v2 Jun 16, 2025
@jorgee jorgee marked this pull request as ready for review June 17, 2025 16:21
@jorgee jorgee requested review from a team as code owners June 17, 2025 16:21
@jorgee
Copy link
Contributor Author

jorgee commented Jun 17, 2025

It is ready for review

@jorgee jorgee requested review from pditommaso and bentsherman June 18, 2025 07:35
Copy link
Member

@pditommaso pditommaso left a comment

Choose a reason for hiding this comment

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

Looks awesome. Made a few minor comments

@jorgee jorgee marked this pull request as draft June 18, 2025 14:02
@jorgee jorgee marked this pull request as draft June 18, 2025 14:02
@jorgee jorgee marked this pull request as draft June 18, 2025 14:02
@jorgee
Copy link
Contributor Author

jorgee commented Jun 18, 2025

I have found an issue with multi-part uploads when uploading large files. I move to draft until I fix it.

Signed-off-by: Ben Sherman <[email protected]>
Signed-off-by: Ben Sherman <[email protected]>
Copy link
Member

@bentsherman bentsherman left a comment

Choose a reason for hiding this comment

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

See my comments above.

I'm mainly confused about the use of the S3 transfer manager vs the old aws.client.upload* config options. The fact that the old options are still used by newOutputStream() , to a user this essentially means "sometimes we use the old options, sometimes we use the new options". It would be nice to deprecate these old options altogether.

It's also not clear to me how to select the async client vs sync client, as I don't see any explicit config option for this

@jorgee
Copy link
Contributor Author

jorgee commented Jul 2, 2025

It's also not clear to me how to select the async client vs sync client, as I don't see any explicit config option for this

The sync client is used in all api call except the S3 transfer manager actions ( S3 copies, uploads and downloads). For transfer manager there is no option to use the sync client. I didn't changed all calls to async because it implied a lot of changes in the code

@jorgee
Copy link
Contributor Author

jorgee commented Jul 2, 2025

I have added a subsection in the aws part of the documentation mentioning the most important changes in the v1 to v2 migration

Signed-off-by: Paolo Di Tommaso <[email protected]>
@pditommaso
Copy link
Member

End to end tests fail badly. I've got this message

ERROR ~ Type com.amazonaws.services.batch.model.KeyValuePair not present

unfortunately I'm not able to retried the full log

@jorgee
Copy link
Contributor Author

jorgee commented Jul 4, 2025

End to end tests fail badly. I've got this message

ERROR ~ Type com.amazonaws.services.batch.model.KeyValuePair not present

unfortunately I'm not able to retried the full log

The package is from v1. I am going to check if there is some place where I forgot someplace where the package is used

@jorgee
Copy link
Contributor Author

jorgee commented Jul 4, 2025

I have realized the gradle tasks implementation inside nexflow that still uses v1. I am going to change it, but I think it shouldn't affect end-to-end tests.

@pditommaso
Copy link
Member

Yep, should be unrelated

@pditommaso pditommaso force-pushed the aws-sdk-v2-fs-impl branch from 74f53d2 to fd86ab4 Compare July 6, 2025 08:29
@pditommaso
Copy link
Member

I've realised some method like AwsBatchTaskHandle.configJobDefRequest were returning a Builder object, that in SDK v2 is an immutable object, making hard for sub-classes (like nf-xpack) to extend the handler behaviour.

To address this, I've introduced a custom class to model the Job definition request

. This allows to better bridge the new builder API model with the existing behaviour and minimise the changes.

@pditommaso
Copy link
Member

Merging this seems com.amazonaws.services.batch.model.KeyValuePair seems related to the xpack that requires to be updated.

@jorgee let's discuss the plan for supporting the option signerOverride if needed.

@pditommaso pditommaso merged commit fc99b44 into master Jul 6, 2025
23 checks passed
@pditommaso pditommaso deleted the aws-sdk-v2-fs-impl branch July 6, 2025 09:22
@pditommaso
Copy link
Member

I saw an integration test into another PR failing with this error

Jul-06 09:44:25.772 [main] DEBUG nextflow.Session - Session aborted -- Cause: The request content has fewer bytes than the specified content-length: 68 bytes.
Jul-06 09:44:25.793 [main] ERROR nextflow.cli.Launcher - @unknown
java.lang.IllegalStateException: The request content has fewer bytes than the specified content-length: 68 bytes.
	at software.amazon.awssdk.core.internal.io.SdkLengthAwareInputStream.read(SdkLengthAwareInputStream.java:82)
	at software.amazon.awssdk.core.io.SdkFilterInputStream.read(SdkFilterInputStream.java:66)
	at software.amazon.awssdk.http.auth.aws.internal.signer.io.ChecksumInputStream.read(ChecksumInputStream.java:52)
	at software.amazon.awssdk.http.auth.aws.internal.signer.chunkedencoding.ChunkedEncodedInputStream.read(ChunkedEncodedInputStream.java:136)
	at software.amazon.awssdk.http.auth.aws.internal.signer.chunkedencoding.ChunkedEncodedInputStream.getChunk(ChunkedEncodedInputStream.java:112)
	at software.amazon.awssdk.http.auth.aws.internal.signer.chunkedencoding.ChunkedEncodedInputStream.currentChunk(ChunkedEncodedInputStream.java:95)
	at software.amazon.awssdk.http.auth.aws.internal.signer.chunkedencoding.ChunkedEncodedInputStream.read(ChunkedEncodedInputStream.java:90)
	at java.base/java.io.FilterInputStream.read(FilterInputStream.java:132)
	at software.amazon.awssdk.core.internal.io.SdkLengthAwareInputStream.read(SdkLengthAwareInputStream.java:75)
	at org.apache.http.entity.InputStreamEntity.writeTo(InputStreamEntity.java:140)
	at software.amazon.awssdk.http.apache.internal.RepeatableInputStreamRequestEntity.writeTo(RepeatableInputStreamRequestEntity.java:157)
	at org.apache.http.impl.execchain.RequestEntityProxy.writeTo(RequestEntityProxy.java:121)
	at org.apache.http.impl.DefaultBHttpClientConnection.sendRequestEntity(DefaultBHttpClientConnection.java:156)
	at org.apache.http.impl.conn.CPoolProxy.sendRequestEntity(CPoolProxy.java:152)
	at org.apache.http.protocol.HttpRequestExecutor.doSendRequest(HttpRequestExecutor.java:238)
	at org.apache.http.protocol.HttpRequestExecutor.execute(HttpRequestExecutor.java:123)
	at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:272)
	at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:186)
	at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:185)
	at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:83)
	at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:56)
	at software.amazon.awssdk.http.apache.internal.impl.ApacheSdkHttpClient.execute(ApacheSdkHttpClient.java:72)
	at software.amazon.awssdk.http.apache.ApacheHttpClient.execute(ApacheHttpClient.java:259)
	at software.amazon.awssdk.http.apache.ApacheHttpClient.access$600(ApacheHttpClient.java:104)
	at software.amazon.awssdk.http.apache.ApacheHttpClient$1.call(ApacheHttpClient.java:236)
	at software.amazon.awssdk.http.apache.ApacheHttpClient$1.call(ApacheHttpClient.java:233)
	at software.amazon.awssdk.core.internal.util.MetricUtils.measureDurationUnsafe(MetricUtils.java:102)
	at software.amazon.awssdk.core.internal.http.pipeline.stages.MakeHttpRequestStage.executeHttpRequest(MakeHttpRequestStage.java:88)
	at software.amazon.awssdk.core.internal.http.pipeline.stages.MakeHttpRequestStage.execute(MakeHttpRequestStage.java:64)
	at software.amazon.awssdk.core.internal.http.pipeline.stages.MakeHttpRequestStage.execute(MakeHttpRequestStage.java:46)
	at software.amazon.awssdk.core.internal.http.pipeline.RequestPipelineBuilder$ComposingRequestPipelineStage.execute(RequestPipelineBuilder.java:206)
	at software.amazon.awssdk.core.internal.http.pipeline.RequestPipelineBuilder$ComposingRequestPipelineStage.execute(RequestPipelineBuilder.java:206)
	at software.amazon.awssdk.core.internal.http.pipeline.RequestPipelineBuilder$ComposingRequestPipelineStage.execute(RequestPipelineBuilder.java:206)
	at software.amazon.awssdk.core.internal.http.pipeline.RequestPipelineBuilder$ComposingRequestPipelineStage.execute(RequestPipelineBuilder.java:206)
	at software.amazon.awssdk.core.internal.http.pipeline.stages.ApiCallAttemptTimeoutTrackingStage.execute(ApiCallAttemptTimeoutTrackingStage.java:74)
	at software.amazon.awssdk.core.internal.http.pipeline.stages.ApiCallAttemptTimeoutTrackingStage.execute(ApiCallAttemptTimeoutTrackingStage.java:43)
	at software.amazon.awssdk.core.internal.http.pipeline.stages.TimeoutExceptionHandlingStage.execute(TimeoutExceptionHandlingStage.java:79)
	at software.amazon.awssdk.core.internal.http.pipeline.stages.TimeoutExceptionHandlingStage.execute(TimeoutExceptionHandlingStage.java:41)
	at software.amazon.awssdk.core.internal.http.pipeline.stages.ApiCallAttemptMetricCollectionStage.execute(ApiCallAttemptMetricCollectionStage.java:55)
	at software.amazon.awssdk.core.internal.http.pipeline.stages.ApiCallAttemptMetricCollectionStage.execute(ApiCallAttemptMetricCollectionStage.java:39)
	at software.amazon.awssdk.core.internal.http.pipeline.stages.RetryableStage.executeRequest(RetryableStage.java:93)
	at software.amazon.awssdk.core.internal.http.pipeline.stages.RetryableStage.execute(RetryableStage.java:56)
	at software.amazon.awssdk.core.internal.http.pipeline.stages.RetryableStage.execute(RetryableStage.java:36)
	at software.amazon.awssdk.core.internal.http.pipeline.RequestPipelineBuilder$ComposingRequestPipelineStage.execute(RequestPipelineBuilder.java:206)
	at software.amazon.awssdk.core.internal.http.StreamManagingStage.execute(StreamManagingStage.java:53)
	at software.amazon.awssdk.core.internal.http.StreamManagingStage.execute(StreamManagingStage.java:35)
	at software.amazon.awssdk.core.internal.http.pipeline.stages.ApiCallTimeoutTrackingStage.executeWithTimer(ApiCallTimeoutTrackingStage.java:82)
	at software.amazon.awssdk.core.internal.http.pipeline.stages.ApiCallTimeoutTrackingStage.execute(ApiCallTimeoutTrackingStage.java:62)
	at software.amazon.awssdk.core.internal.http.pipeline.stages.ApiCallTimeoutTrackingStage.execute(ApiCallTimeoutTrackingStage.java:43)
	at software.amazon.awssdk.core.internal.http.pipeline.stages.ApiCallMetricCollectionStage.execute(ApiCallMetricCollectionStage.java:50)
	at software.amazon.awssdk.core.internal.http.pipeline.stages.ApiCallMetricCollectionStage.execute(ApiCallMetricCollectionStage.java:32)
	at software.amazon.awssdk.core.internal.http.pipeline.RequestPipelineBuilder$ComposingRequestPipelineStage.execute(RequestPipelineBuilder.java:206)
	at software.amazon.awssdk.core.internal.http.pipeline.RequestPipelineBuilder$ComposingRequestPipelineStage.execute(RequestPipelineBuilder.java:206)
	at software.amazon.awssdk.core.internal.http.pipeline.stages.ExecutionFailureExceptionReportingStage.execute(ExecutionFailureExceptionReportingStage.java:37)
	at software.amazon.awssdk.core.internal.http.pipeline.stages.ExecutionFailureExceptionReportingStage.execute(ExecutionFailureExceptionReportingStage.java:26)
	at software.amazon.awssdk.core.internal.http.AmazonSyncHttpClient$RequestExecutionBuilderImpl.execute(AmazonSyncHttpClient.java:210)
	at software.amazon.awssdk.core.internal.handler.BaseSyncClientHandler.invoke(BaseSyncClientHandler.java:103)
	at software.amazon.awssdk.core.internal.handler.BaseSyncClientHandler.doExecute(BaseSyncClientHandler.java:173)
	at software.amazon.awssdk.core.internal.handler.BaseSyncClientHandler.lambda$execute$1(BaseSyncClientHandler.java:80)
	at software.amazon.awssdk.core.internal.handler.BaseSyncClientHandler.measureApiCallSuccess(BaseSyncClientHandler.java:182)
	at software.amazon.awssdk.core.internal.handler.BaseSyncClientHandler.execute(BaseSyncClientHandler.java:74)
	at software.amazon.awssdk.core.client.handler.SdkSyncClientHandler.execute(SdkSyncClientHandler.java:45)
	at software.amazon.awssdk.awscore.client.handler.AwsSyncClientHandler.execute(AwsSyncClientHandler.java:53)
	at software.amazon.awssdk.services.s3.DefaultS3Client.putObject(DefaultS3Client.java:11228)
	at software.amazon.awssdk.services.s3.DelegatingS3Client.lambda$putObject$86(DelegatingS3Client.java:9145)
	at software.amazon.awssdk.services.s3.internal.crossregion.S3CrossRegionSyncClient.invokeOperation(S3CrossRegionSyncClient.java:67)
	at software.amazon.awssdk.services.s3.DelegatingS3Client.putObject(DelegatingS3Client.java:9145)
	at nextflow.cloud.aws.nio.S3OutputStream.putObject(S3OutputStream.java:626)
	at nextflow.cloud.aws.nio.S3OutputStream.putObject(S3OutputStream.java:581)
	at nextflow.cloud.aws.nio.S3OutputStream.close(S3OutputStream.java:384)
	at java.base/java.io.FilterOutputStream.close(FilterOutputStream.java:188)
	at org.codehaus.groovy.vmplugin.v8.IndyInterface.fromCache(IndyInterface.java:321)
	at nextflow.extension.FilesEx.closeQuietly(FilesEx.groovy:689)
	at nextflow.cache.CloudCacheStore.close(CloudCacheStore.groovy:93)
	at nextflow.cache.CacheDB.close(CacheDB.groovy:266)
	at nextflow.Session.destroy(Session.groovy:724)
	at nextflow.script.ScriptRunner.shutdown(ScriptRunner.groovy:263)
	at nextflow.script.ScriptRunner.execute(ScriptRunner.groovy:147)
	at nextflow.cli.CmdRun.run(CmdRun.groovy:379)
	at nextflow.cli.Launcher.run(Launcher.groovy:513)
	at nextflow.cli.Launcher.main(Launcher.groovy:673)

Worth double checking

@jorgee
Copy link
Contributor Author

jorgee commented Jul 7, 2025

It could be caused because in v2 content-length is now part of the request instead of metadata. The strange part is that it is not always happening. I am going to try to find when it is reproduced.

@bentsherman bentsherman added this to the 25.10 milestone Jul 15, 2025
@bentsherman bentsherman linked an issue Jul 15, 2025 that may be closed by this pull request
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Migrate to AWS Java SDK v2
4 participants