[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 1 commit 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.
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.
The fluss-fs-s3 uber-jar bundles hadoop-common and aws-java-sdk-* dependencies without relocations for jackson and several other bundled third-party packages. These classes can shadow a downstream application's own dependencies and cause runtime failures such as NoSuchMethodError or NoClassDefFoundError. Relocate the transitively-bundled third-party packages into the shared org.apache.fluss.shaded.* namespace, aligned with the pre-existing org.apache.commons relocation already on main: - com.fasterxml -> org.apache.fluss.shaded.com.fasterxml - org.apache.htrace -> org.apache.fluss.shaded.org.apache.htrace - com.ctc -> org.apache.fluss.shaded.com.ctc - com.google.common / com.google.re2j / com.google.j2objc / com.google.thirdparty -> org.apache.fluss.shaded.com.google.* (narrowed from the broad `com.google` namespace, so references to un-bundled protobuf/gson are NOT rewritten to a dangling path) - org.apache.commons -> only the 9 actually-bundled packages (beanutils, codec, collections, compress, configuration2, io, lang3, logging, text), narrowed from the broad `org.apache.commons` namespace so references to un-bundled commons-cli / commons-jxpath / commons-math3 etc. are NOT rewritten to a dangling path - org.codehaus -> only stax2 and mojo, the two org.codehaus packages actually bundled in this jar (124 + 1 classes). org.codehaus.jackson is referenced by Hadoop (e.g. MetricsJsonBuilder) but is NOT packaged here, so it is left un-relocated and preserves the pre-change external classpath resolution META-INF/versions/**/com/fasterxml/** is excluded from relocation: the Shade plugin relocates base-path classes but not Multi-Release JAR entries (same issue class as apache#3553, which affected fluss-shaded-jackson). Verified by inspecting the produced jar (shaded-entry scan + dangling-reference scan of the class constant pools): - 0 unshaded com/fasterxml entries (base path and MRJ); 1,050 jackson classes correctly relocated to org/apache/fluss/shaded/com/fasterxml/* - no dangling class references: protobuf/gson, commons-cli/jxpath/math3, and org.codehaus.jackson are not rewritten. The scanner's two remaining reports are commons-codec resource paths, and both resources are present at their relocated paths Closes apache#4072
a742c13 to
46fd497
Compare
Thanks @binary-signal for the detailed analysis and for adding the checker in The broad After a clean rebuild, the jar contains 0 unshaded jackson entries and all I'll keep #4073 scoped to fs-s3 and leave the broader filesystem-family work |
Purpose
Linked issue: close #4072
The
fluss-fs-s3uber-jar bundleshadoop-commonandaws-java-sdk-*dependencies without relocations for jackson and several other bundled
third-party packages, leaking 1,050 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, consolidated into the single
<relocations>block andnamespaced under
org.apache.fluss.shaded.*(aligned with thepre-existing
org.apache.commonsrelocation onmain):com.fasterxml→org.apache.fluss.shaded.com.fasterxmlorg.apache.htrace→org.apache.fluss.shaded.org.apache.htracecom.ctc→org.apache.fluss.shaded.com.ctccom.google.common/com.google.re2j/com.google.j2objc/com.google.thirdparty→org.apache.fluss.shaded.com.google.*(narrowed from the broad
com.googlenamespace so references toun-bundled
protobuf/gsonare not rewritten to a dangling path)org.apache.commons→ only the 9 actually-bundled packages:beanutils,codec,collections,compress,configuration2,io,lang3,logging,text(narrowed from the broadorg.apache.commonsnamespace so references to un-bundledcommons-cli/commons-jxpath/commons-math3etc. are notrewritten to a dangling path)
org.codehaus→ onlystax2andmojo, the twoorg.codehauspackages actually bundled in this jar (124 + 1 classes).
org.codehaus.jacksonis referenced by Hadoop (e.g.MetricsJsonBuilder) but is not packaged here, so it is leftun-relocated, preserving the pre-change external classpath resolution
<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:
com/fasterxmlclass entriescom/fasterxmlentries (base path and MRJ);1,050 jackson classes correctly relocated to
org/apache/fluss/shaded/com/fasterxml/*protobuf/gson,commons-cli/jxpath/math3, andorg.codehaus.jacksonare notrewritten. The checker reports two non-class commons-codec resource
strings (
language/bm/andlanguage/dmrules); both resources are presentat their relocated paths in the produced jar
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.shadednamespace.Documentation
No new feature introduced; no documentation changes required.