[build] Relocate shaded jackson and other internal deps in fluss-fs-s3 - #4073
[build] Relocate shaded jackson and other internal deps in fluss-fs-s3#4073Jackeyzhe wants to merge 2 commits into
Conversation
| and not used / exposed downstream, mirroring the pattern | ||
| of fluss-fs-hadoop-shaded --> | ||
| <relocation> | ||
| <pattern>com.google.re2j</pattern> |
There was a problem hiding this comment.
would it be better to shaded the whole com.google(including re2j and common)?
There was a problem hiding this comment.
Good catch — agreed, widened to the whole com.google namespace
(a6215ed). It turns out the jar was still leaking 1,974 unshaded
com.google.common (Guava) entries plus j2objc / thirdparty, which
is the same shadowing bug class as the jackson leak in #4072.
Verified: 0 unshaded com/google entries (base path and MRJ), all
constant-pool references rewritten to the shaded path, and hadoop's
own org.apache.hadoop.thirdparty.com.google namespace left untouched.
While verifying I also noticed fluss-fs-hdfs has the same problem
at a larger scale (1,073 unshaded com/fasterxml + 2,731 com/google
in its uber-jar) — I'll file a separate issue for that so this PR
stays scoped to fluss-fs-s3.
The fluss-fs-s3 uber-jar bundles hadoop-common and aws-java-sdk dependencies without any shade relocations, leaking 1,114 unshaded jackson class files (jackson-databind, jackson-core, jackson-annotations, jackson-dataformat-cbor) at com/fasterxml/* paths. This can shadow downstream applications' jackson-core and cause NoSuchMethodError at runtime. Fix by adding the same relocation pattern used by fluss-fs-hadoop-shaded: - com.fasterxml -> org.apache.fluss.fs.shaded.s3.com.fasterxml - org.codehaus -> org.apache.fluss.fs.shaded.s3.org.codehaus - com.ctc -> org.apache.fluss.fs.shaded.s3.com.ctc - com.google.re2j -> org.apache.fluss.fs.shaded.s3.com.google.re2j - org.apache.htrace -> org.apache.fluss.fs.shaded.s3.org.apache.htrace Also excludes META-INF/versions/**/com/fasterxml/** because the shade plugin relocates base-path classes but not Multi-Release JAR entries (same issue class as apache#3553). Verified: 0 unshaded com/fasterxml entries (base path and MRJ), 1,093 jackson classes correctly relocated, bytecode references in AWS SDK/Hadoop classes rewritten to the shaded path, intentional jaxb-api MRJ entries preserved. Closes apache#4072
Per review feedback, widen the com.google.re2j relocation to the full com.google namespace. The jar previously still leaked 1,974 unshaded com.google.common (Guava) entries plus j2objc/thirdparty, which is the same shadowing bug class as the jackson leak in apache#4072. Verified: 0 unshaded com/google entries (base path and MRJ), all constant-pool references to com.google.* rewritten to the shaded path, hadoop's own org.apache.hadoop.thirdparty.com.google namespace left untouched.
a6215ed to
a742c13
Compare
There was a problem hiding this comment.
@Jackeyzhe thank you for addressing this issue, I've left some comments
…gins PR apache#4073 relocated the packages Hadoop and the cloud SDKs drag into the fluss-fs-s3 uber-jar, but the six sibling plugins bundle the same dependencies and leaked them identically: roughly 1000 jackson classes each, plus codehaus, ctc, re2j and around 1950 guava. Applies the same relocation set to azure, cos, obs, oss and hdfs, each under its own org.apache.fluss.fs.shaded.<plugin> namespace so two plugins on one classpath cannot collide. fluss-fs-gs relocates com.google.common and com.google.re2j individually rather than the whole com.google namespace: it names com.google.cloud.hadoop.fs.gcs.GoogleHadoopFileSystem by string in its own source and carries com.google entries in META-INF/services, so rewriting the GCS SDK's package would break resolution by class name. Netty is deliberately left unrelocated. These jars ship 20-25 native library entries, and netty resolves those by a name derived from its own package, so relocating it also requires renaming the native binaries -- out of scope here and a silent runtime failure if done incompletely. Verified: mvn test passes for all six, and the leak checker reports zero unshaded jackson/codehaus/ctc/re2j/guava with matching relocated counts.
Each of these four shipped 1698 unshaded io.netty classes, pulled in by the cloud SDK's HTTP layer, able to shadow an engine's own netty. Relocating netty is established practice here: fluss-metrics-influxdb already does it with a plain <relocation>, and fluss-common consumes netty through the pre-shaded fluss-shaded-netty artifact. Neither needed special handling, and none of these four bundle a single netty native. io.netty.internal.tcnative is excluded from the relocation. tcnative is netty's JNI wrapper around OpenSSL, so its native symbols bind to that package name and renaming it would break them -- the same failure mode that rules out relocating org.apache.arrow in fluss-lake-lance. It is referenced here but not bundled, so leaving it alone both avoids references that resolve nowhere and keeps OpenSSL usable when the host supplies it. Verified in the built jar: 0 references to a shaded tcnative, 73 to the original. An earlier note in this branch claimed these jars ship netty natives and that this made relocation unsafe. That was wrong: the natives present are snappy's and Arrow/Lance JNI, and there are no netty natives at all. Verified: 264 tests pass across the filesystem modules, and no new dangling relocated references (the protobuf, jxpath and codec ones listed by --dangling are pre-existing, from the broad com.google and org.apache.commons patterns inherited from apache#4073 and apache#3960). All four plugins stop leaking netty.
|
I applied this PR locally on top of One thing worth addressing before merge, though.
|
| package | classes bundled |
|---|---|
com.google.common |
1974 |
com.google.re2j |
48 |
com.google.j2objc |
14 |
com.google.thirdparty |
5 |
But the bundled Hadoop and AWS SDK code references com.google.protobuf and com.google.gson, neither of which is packaged (both are <exclusion>s in this pom). After this PR, those references are rewritten to org.apache.fluss.fs.shaded.s3.com.google.protobuf.* — about 40 distinct classes, e.g.:
org/apache/fluss/fs/shaded/s3/com/google/protobuf/Message (11 refs)
org/apache/fluss/fs/shaded/s3/com/google/protobuf/Descriptors (5 refs)
org/apache/fluss/fs/shaded/s3/com/google/protobuf/GeneratedMessage (5 refs)
org/apache/fluss/fs/shaded/s3/com/google/gson/stream/JsonReader (1 ref)
Before relocation, com.google.protobuf.Message is a soft dependency: absent from the uber-jar, but resolvable if the surrounding classpath or plugin parent classloader provides it. After relocation the reference points at a coordinate nothing will ever provide, because no artifact anywhere publishes org.apache.fluss.fs.shaded.s3.com.google.protobuf. A previously-satisfiable link becomes a guaranteed NoClassDefFoundError on any code path that touches it.
This is not hypothetical. I hit exactly this mechanism via the sibling org.apache.commons relocation (added in #3960, already on main) while extending this PR's approach to the other filesystem plugins:
java.lang.NoClassDefFoundError: org/apache/fluss/shaded/org/apache/commons/cli/ParseException
at org.apache.hadoop.hdfs.server.namenode.NameNode.createNameNode(NameNode.java:1713)
at org.apache.hadoop.hdfs.MiniDFSCluster.createNameNode(MiniDFSCluster.java:1374)
...
at org.apache.fluss.fs.hdfs.HdfsBehaviorTest.createHDFS(HdfsBehaviorTest.java:62)
commons-cli was on the classpath. The reference had simply been rewritten to a name that does not exist.
To make this checkable , #4144 adds tools/ci/check_shaded_jars.py, whose --dangling mode reports exactly this: relocated class references a jar does not contain. Running it on fluss-fs-s3 built from this PR lists 92 such classes, 83 of them real classes rather than annotations.
Suggested change
Name the packages that are actually bundled rather than the com.google prefix:
<relocation>
<pattern>com.google.common</pattern>
<shadedPattern>org.apache.fluss.fs.shaded.s3.com.google.common</shadedPattern>
</relocation>
<relocation>
<pattern>com.google.j2objc</pattern>
<shadedPattern>org.apache.fluss.fs.shaded.s3.com.google.j2objc</shadedPattern>
</relocation>
<relocation>
<pattern>com.google.thirdparty</pattern>
<shadedPattern>org.apache.fluss.fs.shaded.s3.com.google.thirdparty</shadedPattern>
</relocation>
<relocation>
<pattern>com.google.re2j</pattern>
<shadedPattern>org.apache.fluss.fs.shaded.s3.com.google.re2j</shadedPattern>
</relocation>This keeps the full guava fix (the actual win here — 1974 classes) and drops the dangling protobuf/gson links. It also matches what fluss-fs-hadoop-shaded already does, which the PR description cites as the pattern being mirrored: that module relocates com.google.re2j specifically, not com.google.
The same applies to the existing org.apache.commons relocation from #3960, which leaves dangling references to commons-cli, commons-codec, commons-math3, commons-net, commons-jxpath, commons-jexl2 and commons-vfs2 in every filesystem plugin. That is pre-existing rather than something this PR introduces; it is written up in #4143 along with the rest of the shading leaks.
#4144 applies the narrow-pattern approach across the modules #4143 covers, if a worked example is useful — every relocation there names the packages actually bundled, and io.netty.internal.tcnative and org.apache.arrow are excluded for the JNI reason above. It deliberately leaves fluss-fs-s3 to this PR.
Two notes for anyone verifying locally
cleanis required. An incremental build reuses already-relocated classes intarget/classes, so a narrowed pattern appears to have no effect until you runmvn clean. This cost me a couple of confusing debug cycles.mvn test -pl fluss-filesystems -amdfails onHadoopUtilsTestindependently of any of this: in a reactor containingfluss-fs-hadoop-shaded, dependent modules get its unshadedtarget/classesbecauseshaderuns atpackage. Useverifyorinstallinstead. Reproduces on unmodifiedmain.
The MRJ exclusion in this PR is also worth keeping even alongside #3884's global one — #3884 puts it in the root pom under <filters combine.children="append">, so it already cascades here, but the local copy keeps this PR correct on its own.
Purpose
Linked issue: close #4072
The
fluss-fs-s3uber-jar bundleshadoop-commonandaws-java-sdk-*dependencies without any shade relocations, leaking 1,114 unshaded
jackson class files (
jackson-databind,jackson-core,jackson-annotations,jackson-dataformat-cbor) atcom/fasterxml/*paths. These can shadow a downstream application's own jackson-core
(e.g. 2.16+) and cause runtime failures such as
NoSuchMethodError.Brief change log
fluss-filesystems/fluss-fs-s3/pom.xml:packages, namespaced under
org.apache.fluss.fs.shaded.s3.*(mirroring the pattern already used by
fluss-fs-hadoop-shaded):com.fasterxml→org.apache.fluss.fs.shaded.s3.com.fasterxmlorg.codehaus→org.apache.fluss.fs.shaded.s3.org.codehauscom.ctc→org.apache.fluss.fs.shaded.s3.com.ctccom.google.re2j→org.apache.fluss.fs.shaded.s3.com.google.re2jorg.apache.htrace→org.apache.fluss.fs.shaded.s3.org.apache.htrace<filter>excludingMETA-INF/versions/**/com/fasterxml/**:the Maven Shade Plugin relocates base-path classes but not
Multi-Release JAR entries (same issue class as [fluss-client][ fluss-flink-*] Uber-jars leak com.fasterxml.jackson.core alongside the shaded copy #3553, which affected
fluss-shaded-jackson). The relocated base-path implementation issufficient for all Java versions.
Tests
Build-only change (pom.xml), verified by inspecting the produced jar:
unzip -l fluss-fs-s3-*.jar | grep -c " com/fasterxml"→ 1,114 unshaded entries
com/fasterxmlentries (base path and MRJ);1,093 jackson classes correctly relocated to
org/apache/fluss/fs/shaded/s3/com/fasterxml/*rewritten to the shaded path (verified via
javapon sampled classes)jaxb-apiMRJ entries underMETA-INF/versions/**arepreserved untouched
./mvnw spotless:checkpassesAPI and Format
No API or storage format changes. This only affects the internal shading
layout of the
fluss-fs-s3uber-jar; all relocated classes move underthe
org.apache.fluss.fs.shaded.s3namespace.Documentation
No new feature introduced; no documentation changes required.