loustler opened a new pull request, #11648:
URL: https://github.com/apache/seatunnel/pull/11648

   Closes #9599
   Closes #9787
   
   This upgrades the shaded Hadoop uber jar from 3.1.4 to 3.4.3 and, 
necessarily with it, moves S3A from AWS SDK v1 to v2. I ran into this while 
upgrading a production cluster, so most of the claims below come with 
measurements attached; I've tried to show the evidence rather than just assert 
it. I'd very much welcome correction where I've misread something.
   
   ## Why
   
   Two dependency-lifecycle reasons, not features:
   
   1. **Hadoop 3.1.4 is long out of the Hadoop project's active release lines** 
— it is the last 3.1.x release, and 3.1 has had no updates since. #9599 asked 
for the move on the grounds that 3.4.x carries fewer known CVEs.
   2. **AWS SDK for Java v1 reached end-of-support on 2025-12-31.** SeaTunnel 
still ships it, because `hadoop-aws` 3.1.4 depends on 
`com.amazonaws:aws-java-sdk-bundle`. This is #9787.
   
   These look like two tasks but are one. As the discussion in #9599 already 
notes, Hadoop 3.4's S3A *is* the migration from AWS SDK v1 to v2 — the Hadoop 
upgrade cannot be taken without the SDK upgrade.
   
   A small note on #9599: it is currently closed as completed, but I may be 
missing where that landed — `dev` still contains 
`seatunnel-shade/seatunnel-hadoop3-3.1.4-uber` with 
`<hadoop3.version>3.1.4</hadoop3.version>`, and 
`<hadoop-aws.version>3.1.4</hadoop-aws.version>` in the root pom, and the 
attempt in #9669 was closed unmerged. If it was closed by mistake, would you 
consider reopening it? Otherwise please feel free to drop the `Closes` line for 
it.
   
   ## Why the two Hadoop versions must move together
   
   `hadoop-aws` declares `hadoop-common` as `provided` at every version, so at 
runtime it links against whatever `hadoop-common` the uber jar ships. They are 
**not independently upgradable**. Bumping only `hadoop3.version` breaks S3A 
outright — verified by resolving every external class and Hadoop-API member 
reference of `seatunnel-hadoop-aws.jar` against the real runtime classpath:
   
   | uber / hadoop-aws | unconditional breaks | conditional |
   |---|---|---|
   | 3.4.3 / 3.1.4 | **2** | — |
   | 3.4.3 / 3.3.6 | 0 | 3 |
   | **3.4.3 / 3.4.3** | **0** | **0** |
   
   The two unconditional failures at 3.4.3/3.1.4:
   
   - `NoClassDefFoundError: org/apache/commons/lang/StringUtils` in 
`S3AFileSystem.initialize()` — `hadoop-common` dropped commons-lang 2.x after 
3.1.4, so nothing on the classpath supplies it. Fails on *any* S3A open.
   - `NoSuchMethodError` on `SemaphoredDelegatingExecutor.<init>` in 
`S3AFileSystem.create()` — the guava-typed constructor was removed. Fails on 
*every* S3A write. Cannot be fixed by adding a dependency; the bytecode calls a 
constructor that no longer exists.
   
   For that reason this PR moves `hadoop3.version` and `hadoop-aws.version` 
together to 3.4.3, rather than bumping the uber jar alone.
   
   ## Changes
   
   ### 1. Hadoop 3.1.4 → 3.4.3
   
   - `seatunnel-shade/seatunnel-hadoop3-3.1.4-uber` → 
**`seatunnel-hadoop3-3.4.3-uber`** (directory + `artifactId`, following the 
existing naming convention). 92 references across 56 files.
   - `hadoop3.version` → `3.4.3`, `hadoop-aws.version` → `3.4.3`.
   - `finalName` and the build-helper artifact path now derive from 
`${hadoop3.version}` instead of repeating the literal, so the next bump is one 
line.
   - `maven-shade-plugin` pinned to 3.6.2 **for this module only**: 
`hadoop-client` 3.4.3 pulls `org.bouncycastle:bcprov-jdk18on`, a multi-release 
jar with `META-INF/versions/21` classes at class file major version 65, which 
the ASM bundled with older shade releases cannot read (`Unsupported class file 
major version 65`).
   
   > **A suggestion, deliberately not done here:** a version-neutral 
`seatunnel-hadoop3-uber` artifactId would remove what seems to be the recurring 
cause of these upgrade PRs going stale, since every bump currently renames a 
module and touches ~56 files. I kept the version in the name to respect the 
existing convention, but I'd be glad to change it if maintainers would prefer 
that — please just say so.
   
   ### 2. AWS SDK v1 → v2
   
   - Root pom `software.amazon.awssdk.version` `2.31.30` → **`2.35.4`**, 
matching the `aws-java-sdk-v2.version` that `hadoop-project` 3.4.3 pins.
   
     This alignment matters and is the concern raised in #9787. `hadoop-aws` 
3.4.x depends on `software.amazon.awssdk:bundle`, so the shaded 
`seatunnel-hadoop-aws.jar` lands in `lib/` next to 
`seatunnel-transforms-v2.jar`, which already bundles `bedrockruntime` + 
`apache-client`. Measured on the built distribution: **396,759** vs **3,732** 
`software/amazon/awssdk/**` entries, sharing `core`, `http`, `regions`, `auth`, 
`awscore` and `utils`. If the versions differ, classpath order decides which 
copy of that shared infrastructure wins. Keeping them equal makes the 
duplicated classes byte-identical, so order stops mattering. This collision did 
not exist under SDK v1 (`com/amazonaws/**` vs `software/amazon/awssdk/**`).
   
   - `checkpoint-storage-hdfs` hardcoded `hadoop-aws` 3.1.4 and 
`aws-java-sdk-bundle` 1.11.271; both now use the shared properties, so they 
cannot silently drift again.
   - `seatunnel-dist` and `assembly-seatunnel-bin-ci.xml`: 
`com.amazonaws:aws-java-sdk-bundle` → `software.amazon.awssdk:bundle`; the 
now-unused `aws-java-sdk.version` property is removed.
   - `S3FileBaseOptions.INSTANCE_PROFILE_CREDENTIALS_PROVIDER`: 
`com.amazonaws.auth.InstanceProfileCredentialsProvider` → 
`org.apache.hadoop.fs.s3a.auth.IAMInstanceCredentialsProvider`.
   
     This one is not optional, unfortunately: that constant is the **default 
value** of `fs.s3a.aws.credentials.provider`, so under SDK v2 every S3 job that 
does not name a provider explicitly would fail to instantiate one. 
`IAMInstanceCredentialsProvider` is Hadoop's own provider, present since 
hadoop-aws 3.3.x and SDK-version-agnostic. S3A's v1-to-v2 adapter cannot help 
here — it needs `com.amazonaws:aws-java-sdk-core`, which `hadoop-aws` declares 
`provided` and is never packaged.
   
     **Existing job configs keep working.** Users write the *enum constant 
name* and `S3HadoopConf` writes `getProvider()` into the Hadoop conf, so 
changing only the mapped class name is config-compatible.
   
   ### 3. Keep log4j 1.x out of the uber jar
   
   This is the one change that is not a version bump, and it is a prerequisite: 
**bumping Hadoop without it ships log4j 1.x into the uber jar.**
   
   Measured on `dev` — `hadoop3.version` set to 3.4.3 with no other change, 
then the built jar inspected at its root paths:
   
   | root path | before this PR | after |
   |---|---|---|
   | `org/apache/log4j/**` | **232** | **0** |
   | `org/slf4j/impl/**` | 0 | 0 |
   
   The 232 entries come from `ch.qos.reload4j:reload4j`, the log4j 1.2.17 fork 
that `hadoop-common` 3.3.3+ depends on under a new GAV. The existing 
`<exclude>log4j:*</exclude>` in the shade `artifactSet` does not match that 
GAV, and neither does the `log4j:log4j` `provided` entry in 
`dependencyManagement`.
   
   This is worth spelling out, because it is easy to read as cosmetic and 
isn't. `lib/*` precedes `seatunnel-starter.jar` on the runtime classpath 
(`bin/seatunnel-cluster.sh`), so reload4j's real `org.apache.log4j.Category` 
**shadows** the one `log4j-1.2-api` contributes to the starter jar. 
Disassembled, the difference is stark:
   
   ```
   seatunnel-starter.jar (log4j-1.2-api shim)     reload4j 1.2.22 (real log4j 
1.x)
     public void callAppenders(LoggingEvent);       public void 
callAppenders(LoggingEvent);
       Code:                                          12: monitorenter
          0: return    // no-op                       26: invokevirtual 
appendLoopOnAppenders
                                                      59: getfield      parent  
 // lock again
   ```
   
   So code using the `org.apache.log4j` API stops reaching log4j2 and instead 
gets an unconfigured log4j 1.x — default root level **DEBUG** — whose 
`callAppenders` takes a monitor on every `Category` up to the shared root. 
(Note the access flags are `ACC_PUBLIC` on *both*: log4j 1.x locks with a 
`synchronized(c)` block inside the method, not a synchronized method. Only the 
body distinguishes them.)
   
   **Proposed fix**: add `ch.qos.reload4j:reload4j` at `provided` scope in 
`dependencyManagement` and `<exclude>ch.qos.reload4j:*</exclude>` to the shade 
`artifactSet`, mirroring how `log4j:log4j` is already handled. The 
`org.apache.log4j` API stays available at runtime through `log4j-1.2-api`, 
correctly routed to log4j2 and governed by `log4j2.properties`.
   
   Additionally, as **defence in depth** (these are no-ops on current `dev`, 
which already manages `org.slf4j:slf4j-reload4j` to `provided`):
   
   - explicit `slf4j-reload4j` / `slf4j-log4j12` exclusions on `hadoop-client` 
— Hadoop's own "No slf4j backends for downstream clients" list still names only 
`slf4j-log4j12`, never updated after HADOOP-18088 switched the backend to 
`slf4j-reload4j` in 3.3.3+;
   - `<exclude>org/slf4j/impl/**</exclude>` in both shade modules, so the 
guarantee holds for a build that does not inherit this parent's 
`dependencyManagement`.
   
     The pattern is deliberately `org/slf4j/impl/**` and **not** 
`org/slf4j/**`: only the binding is excluded, never the SLF4J API. It is also 
anchored at the jar root, so it intentionally leaves the AWS SDK's own 
relocated copy under `software/amazon/awssdk/thirdparty/org/slf4j/**` alone — 
those are different fully-qualified names, inert for SLF4J binding resolution, 
and required for the SDK's internal logging.
   
   ### 4. Declare okhttp 2.x explicitly in `seatunnel-engine-server`
   
   `JobEventHttpReportHandler` is written against the legacy okhttp 2.x API 
(`com.squareup.okhttp.*` — a different groupId *and* Java package from the 
`com.squareup.okhttp3:okhttp` 4.x managed in the root pom), but the module 
never declared it. The classes arrived transitively from `hadoop-hdfs-client`, 
which depended on `com.squareup.okhttp:okhttp` 2.7.5 up to Hadoop 3.1.4, moved 
to `okhttp3` in 3.3.6, and ships neither in 3.4.x.
   
   As far as I can tell this is a latent undeclared-dependency issue on `dev` 
today, which only turns into a build failure once Hadoop moves. Declaring it 
explicitly at 2.7.5 — matching the already-present test-scoped `mockwebserver` 
— is what the reviewer of #10676 suggested for this same problem, so I've 
followed that.
   
   ### 5. Docs
   
   - `docs/en` + `docs/zh`: 50 occurrences of `com.amazonaws.auth.*` class 
names updated across 12 files (`S3File` source/sink, `Hive` source/sink, 
`checkpoint-storage`, `kubernetes/configuration`), plus the `Hadoop 3.1.4` 
references in the provider-contract prose.
   - `docs/{en,zh}/introduction/concepts/incompatible-changes.md`: new entry 
covering the artifactId rename, the SDK v1 → v2 class-name migration table, and 
an explicit note that enum-based configs need no change.
   
   ## Verification
   
   - `mvn package` on the changed modules (`seatunnel-hadoop3-3.4.3-uber`, 
`seatunnel-engine-server`, `connector-file-s3`, `checkpoint-storage-hdfs`) with 
`-am`: **BUILD SUCCESS**.
   - Uber jar at the root paths that matter: `org/slf4j/impl/**` 0, 
`org/slf4j/**` 0, `org/apache/log4j/**` 0 (232 without the guard).
   - Binary compatibility of `seatunnel-hadoop-aws.jar` against the real 4-jar 
runtime classpath at 3.4.3/3.4.3: 485 class references and 471 member 
references resolve; 0 unconditional and 0 conditional breaks.
   - On a full distribution built from an equivalent change set: exactly one 
SLF4J binding on the runtime classpath (`seatunnel-starter.jar`), no `lib/` jar 
carrying a root `org/slf4j/impl/**` or `org/apache/log4j/**`, and 
`IAMInstanceCredentialsProvider` present.
   
   ## Known trade-off: distribution size
   
   `hadoop-aws` 3.4.x depends on `software.amazon.awssdk:bundle`, which 
contains every AWS service client, so the shaded `seatunnel-hadoop-aws.jar` 
grows from ~83 MB to ~658 MB and the binary tarball roughly doubles.
   
   Narrowing the dependency to what S3A actually uses (`s3`, `sts`, `dynamodb`, 
`apache-client` and their shared core) should bring it to roughly 80–120 MB. I 
left that out of this PR on purpose: S3A resolves provider classes by name via 
reflection, so a missing module surfaces only at runtime and would need real S3 
integration testing to validate, which felt like a separate change. If you 
would rather not merge the size increase, I'm happy to take that on in this PR 
instead — I'd appreciate your guidance on which you'd prefer.
   
   ## Pre-existing issue found but not fixed here
   
   `JsonSerialization.getMapper()` in `hadoop-aws` is broken **even with the 
current 3.1.4 uber jar**: the uber jar relocates Jackson to 
`shade.hadoop.com.fasterxml.jackson` while `hadoop-aws` references the original 
package. It affects only `RoleModel` (assumed-role IAM policy JSON). I've left 
it alone as out of scope here, but wanted to flag it in case it's useful.
   
   ## Remaining AWS SDK v1 usage
   
   Two E2E test modules still import `com.amazonaws.*`: 
`connector-databend-e2e` (3 files) and `connector-file-s3-e2e` (1 file). All 
production connector main sources are already on SDK v2 (`amazondynamodb`, 
`amazonsqs`, `iceberg`, `jdbc`, `transforms-v2`), so after this PR the shipped 
artifact should no longer carry SDK v1. I'm happy to convert the test modules 
here or in a follow-up, whichever fits your review preferences better.
   
   ## Does this PR introduce a *user-facing* change?
   
   Yes — documented in `incompatible-changes.md` (en + zh). Two breaking 
changes: the uber jar `artifactId` rename, and `com.amazonaws.*` 
credentials-provider class names no longer resolving. Jobs using the typed enum 
values are unaffected.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to