Repository navigation
fix: prevent duplicate AWS SDK S3 classloading when s3 and iceberg extensions are both loaded - #20409
Conversation
…tensions are both loaded druid-s3-extensions and druid-iceberg-extensions both declare a direct dependency on software.amazon.awssdk:s3, so each is packaged with its own private copy of the S3 SDK jar and loads it through its own extension classloader. software.amazon.awssdk:s3 registers static IdentityProperty constants (e.g. in S3ExpressPlugin) into a shared, SDK-wide registry, so loading two independent copies of the class makes the registry see two IdentityProperty instances with the same namespace/name but different object identity, and it throws: java.lang.IllegalArgumentException: No duplicate IdentityProperty names allowed but both IdentityProperties ... have the same namespace (java.lang.String) and name (Bucket). This can crash an Iceberg batch ingestion task (ExceptionInInitializerError) as soon as both extensions are loaded together, before any subtasks run. Other AWS SDK v2 artifacts that both extensions need (sts, aws-core, sdk-core, auth, identity-spi, ...) are already resolved once, from the shared classpath, via druid-server's runtime dependency on druid-aws-common; software.amazon.awssdk:s3 was simply missing from that list. This promotes it the same way: add it as a runtime dependency of druid-aws-common (so it ships once in the shared /opt/druid/lib, not per-extension), and mark it provided in both extensions so neither one bundles its own copy.
FrankChen021
left a comment
There was a problem hiding this comment.
🟢 Approval recommended
The merge-base diff and relevant surrounding contracts were reviewed for all three changed Maven files. Promoting software.amazon.awssdk:s3 to the shared runtime through druid-aws-common is consistent with druid-server's runtime dependency and Druid's parent-first extension classloader; marking the direct S3 dependencies as provided prevents the two extension packages from contributing private copies while retaining compile and test visibility. I also inspected the pull-deps runtime dependency filter, distribution runtime dependency set, task classpath defaults, all direct S3 declarations in the repository, and the existing AWS SDK license entry. No actionable correctness, compatibility, lifecycle, security, or missing-test issue was found in this static review.
Reviewed 3 of 3 changed files.
This is an automated review by Codex GPT-5.6-Luna(max)
Replace the verbose explanation of why software.amazon.awssdk:s3 is promoted to the shared classpath (in druid-aws-common, druid-s3-extensions, and druid-iceberg-extensions) with a single concise comment, per review feedback on apache#20409. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
FrankChen021
left a comment
There was a problem hiding this comment.
Follow-up assessment
The author addressed the prior review feedback by shortening the shared-classpath comments in all three changed POM files. I reviewed the latest author reply, the current PR head, the incremental and full diffs, and the surrounding Maven dependency/classpath handling; no further inline response or actionable finding is needed.
Reviewed 3 of 3 changed files; 0 findings.
This is an automated review by Codex GPT-5.6-Luna(max)
Replace the verbose explanation of why software.amazon.awssdk:s3 is promoted to the shared classpath (in druid-aws-common, druid-s3-extensions, and druid-iceberg-extensions) with a single concise comment, per review feedback on apache#20409.
8a08a05 to
0401e01
Compare
FrankChen021
left a comment
There was a problem hiding this comment.
🟢 Approval recommended
The current head preserves the intended classpath fix: software.amazon.awssdk:s3 is supplied once through the shared runtime dependency path, while both extension-local declarations remain compile/test-visible but are excluded from extension packaging. I rechecked the empty incremental diff, the full merge-base diff, the three changed POM files, the server-to-aws-common runtime edge, pull-deps' runtime/provided filtering, the parent-first and extension-first loader behavior, all repository S3 consumers/declarations, and the existing AWS SDK v2 license entry. No actionable correctness, compatibility, classloading, packaging, security, or missing-test issue was found in this static review.
Reviewed 3 of 3 changed files.
This is an automated review by Codex GPT-5.6-Luna(max)
#20409 introduced a change that moved the AWS SDK to the parent classloader. This patch moves aws-crt-client as well, since aws-crt-core (in the SDK) relied on classes from aws-crt-client.
Fixes #19636.
Description
When
druid-s3-extensionsanddruid-iceberg-extensionsare both loaded, an Iceberg batch ingestion task that reads S3/Glue can fail before it starts running subtasks, while Iceberg constructs its AWS S3 client, with:Root cause. Both extensions declare their own direct dependency on
software.amazon.awssdk:s3, sopull-depsbundles a private copy of thes3jar into each extension's own directory, and each extension is loaded through its ownStandardURLClassLoader(druid.extensions.useExtensionClassloaderFirstdefaults tofalse, i.e. parent-first delegation).software.amazon.awssdk.services.s3.internal.s3express.S3ExpressPlugin(inside thes3jar) registers a staticIdentityPropertyconstant into a registry that lives inidentity-spi.identity-spiis already loaded once, from the shared parent classpath (/opt/druid/lib), becausedruid-serverhas a runtime dependency ondruid-aws-common, which depends onidentity-spitransitively. Buts3itself is not on that shared classpath, so each extension classloader loads its own copy ofS3ExpressPlugin, and each copy's staticIdentityPropertyfield is a distinct object with the same name/namespace — which the shared, singleton registry rejects as a "duplicate".Fix. This follows the same pattern already used for
sts(see the "WebIdentityTokenProvider requires runtime dependency on sts" comment incloud/aws-common/pom.xml, andstsbeingprovidedin both extensions):software.amazon.awssdk:s3as aruntimedependency ofdruid-aws-common, so it is resolved once, from the shared classpath (druid-server→druid-aws-common→s3), instead of being extension-scoped.software.amazon.awssdk:s3asprovidedin bothextensions-core/s3-extensions/pom.xmlandextensions-contrib/druid-iceberg-extensions/pom.xml, so neither extension bundles its own copy any more (it's still on the compile/test classpath, just not packaged).No
licenses.yamlchange is needed:software.amazon.awssdk:s3(likests,identity-spi,sdk-core, etc.) is already covered by the existing "AWS SDK for Java 2" entry, which ismodule: java-coreand lists the whole SDK, independent of which module physically bundles which artifact.Verification
mvn dependency:tree -Dscope=runtimeoncloud/aws-commonnow showssoftware.amazon.awssdk:s3:jar:2.55.1:runtime.mvn dependency:tree -Dscope=runtimeonextensions-core/s3-extensionsandextensions-contrib/druid-iceberg-extensionsno longer listssoftware.amazon.awssdk:s3anywhere in either module's own subtree (confirmed by grepping the tree output forawssdk:s3:and checking the matches are outside those modules' sections).mvn test-compileforcloud/aws-common,extensions-core/s3-extensions, andextensions-contrib/druid-iceberg-extensions(plus their-amprerequisites) succeeds.druid-s3-extensions: 247 tests, 0 failures, 0 errors.druid-iceberg-extensions: 61 tests, 0 failures, 0 errors (includingGlueIcebergCatalogTest).software.amazon.awssdk:s3andidentity-spijars resolved by this build (2.55.1) and twoURLClassLoaders configured exactly like Druid's defaultStandardURLClassLoader(parent-first delegation, one parent "shared classpath" loader plus two child "extension" loaders): with a private copy ofs3-2.55.1.jargiven to each child loader (today's pre-fix layout), building anS3Clientthrough the second loader reproduces the exact reported exception, verbatim:s3-2.55.1.jarmoved to the shared/parent loader instead (this fix's layout — one copy, both extensions delegate to it), building anS3Clientthrough both loaders succeeds with no exception. This isn't a Druid integration test I could reasonably check in (it stands up its own throwaway classloaders against raw SDK jars rather than a running Druid process), but it directly confirms the classloading mechanism described above and that this change removes it.I did not add real S3/Glue infrastructure to run an actual end-to-end Iceberg ingestion task; the above reproduces the exact failing code path (
S3Client.builder().build()under duplicated extension classloaders) that the issue's stack trace points to.Release note
Fixed: Iceberg batch ingestion tasks no longer fail with
IllegalArgumentException: No duplicate IdentityProperty names allowed ... name (Bucket)whendruid-s3-extensionsanddruid-iceberg-extensionsare loaded together.Key changed/added classes in this PR
cloud/aws-common/pom.xml,extensions-core/s3-extensions/pom.xml,extensions-contrib/druid-iceberg-extensions/pom.xml)This PR has:
This PR was prepared by an AI coding agent (Claude, via Claude Code) under human supervision, from investigation through implementation and verification, as disclosed above; the reproduction methodology and verification results are genuine and were actually executed, not simulated.