Skip to content

[build] Relocate shaded jackson and other internal deps in fluss-fs-s3 - #4073

Open
Jackeyzhe wants to merge 2 commits into
apache:mainfrom
Jackeyzhe:fix-fs-s3-jackson-relocation
Open

[build] Relocate shaded jackson and other internal deps in fluss-fs-s3#4073
Jackeyzhe wants to merge 2 commits into
apache:mainfrom
Jackeyzhe:fix-fs-s3-jackson-relocation

Conversation

@Jackeyzhe

Copy link
Copy Markdown
Contributor

Purpose

Linked issue: close #4072

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. 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:

  • Added shade relocations for the transitively-bundled third-party
    packages, namespaced under org.apache.fluss.fs.shaded.s3.*
    (mirroring the pattern already used by fluss-fs-hadoop-shaded):
    • com.fasterxmlorg.apache.fluss.fs.shaded.s3.com.fasterxml
    • org.codehausorg.apache.fluss.fs.shaded.s3.org.codehaus
    • com.ctcorg.apache.fluss.fs.shaded.s3.com.ctc
    • com.google.re2jorg.apache.fluss.fs.shaded.s3.com.google.re2j
    • org.apache.htraceorg.apache.fluss.fs.shaded.s3.org.apache.htrace
  • Added a <filter> excluding META-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 is
    sufficient for all Java versions.

Tests

Build-only change (pom.xml), verified by inspecting the produced jar:

  • Before fix: unzip -l fluss-fs-s3-*.jar | grep -c " com/fasterxml"
    → 1,114 unshaded entries
  • After fix: 0 unshaded com/fasterxml entries (base path and MRJ);
    1,093 jackson classes correctly relocated to
    org/apache/fluss/fs/shaded/s3/com/fasterxml/*
  • Bytecode references inside the bundled AWS SDK / Hadoop classes are
    rewritten to the shaded path (verified via javap on sampled classes)
  • Intentional jaxb-api MRJ entries under META-INF/versions/** are
    preserved untouched
  • ./mvnw spotless:check passes

API and Format

No API or storage format changes. This only affects the internal shading
layout of the fluss-fs-s3 uber-jar; all relocated classes move under
the org.apache.fluss.fs.shaded.s3 namespace.

Documentation

No new feature introduced; no documentation changes required.

Comment thread fluss-filesystems/fluss-fs-s3/pom.xml Outdated
and not used / exposed downstream, mirroring the pattern
of fluss-fs-hadoop-shaded -->
<relocation>
<pattern>com.google.re2j</pattern>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

would it be better to shaded the whole com.google(including re2j and common)?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Filed #4116 for the fluss-filesystems shading leak (all 7 plugins verified leaked, gs worst)
so #4073 stays scoped to s3 — thanks again for the catch, @litiliu.

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.

@binary-signal binary-signal left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@Jackeyzhe thank you for addressing this issue, I've left some comments

binary-signal added a commit to binary-signal/fluss that referenced this pull request Aug 28, 2026
…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.
binary-signal added a commit to binary-signal/fluss that referenced this pull request Aug 28, 2026
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.
@binary-signal

binary-signal commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

I applied this PR locally on top of main (4e372d5c7) and can confirm it does what it says: fluss-fs-s3 goes from 1050 unshaded com/fasterxml classes to 0, with 1974 guava, 48 re2j, 205 ctc and 125 codehaus classes correctly relocated under org.apache.fluss.fs.shaded.s3.

One thing worth addressing before merge, though.

com.google is broader than what the jar actually bundles

The shade plugin rewrites every reference matching a <relocation> pattern, including references to classes the jar does not contain. fluss-fs-s3 bundles only four com.google.* packages:

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

  • clean is required. An incremental build reuses already-relocated classes in target/classes, so a narrowed pattern appears to have no effect until you run mvn clean. This cost me a couple of confusing debug cycles.
  • mvn test -pl fluss-filesystems -amd fails on HadoopUtilsTest independently of any of this: in a reactor containing fluss-fs-hadoop-shaded, dependent modules get its unshaded target/classes because shade runs at package. Use verify or install instead. Reproduces on unmodified main.

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[build] fluss-fs-s3 uber-jar bundles 1,114 unshaded jackson classes that can shadow downstream jackson

3 participants