morningman opened a new pull request, #66729:
URL: https://github.com/apache/doris/pull/66729

   ### What problem does this PR solve?
   
   Issue Number: close #xxx
   
   Related PR: #xxx
   
   Problem Summary:
   
   **Draft.** Opened for direction review. See "State of this PR" at the bottom 
for what is
   still missing.
   
   Today every `be-java-extensions` module is flattened onto one classpath. 
`bin/start_be.sh`
   puts an 80MB `preload-extensions` fat jar plus `java-udf` in front of 
everything, each
   scanner is a second fat jar loaded by a child-first loader, and BE resolves 
a scanner by
   Java class name. Three consequences, all of which have caused production 
incidents:
   
   - **Silent version arbitration.** Between the shared layer and the scanners 
there are
     45,530 same-named classes, thousands of which differ in bytes (parquet 
2.12 vs 2.10,
     iceberg, hive UDF contracts, two log4j 1.x). Which one runs is decided by 
classpath
     order, with no warning. A scanner cannot upgrade a library without 
changing what every
     other scanner sees.
   - **Implicit provider contracts.** libthrift comes from `java-udf`; 
`org.apache.doris.thrift.*`
     from `preload-extensions`; hadoop from `lib/hadoop_hdfs`. None of this is 
declared
     anywhere - the only record is pom comments left behind by past incidents, 
written in the
     providing module rather than the consuming one.
   - **A JVM nobody asked for.** BE creates a JVM at startup whether or not any 
Java code is
     ever used, and a broken jar in the shared layer can stop BE from starting.
   
   This PR replaces that with Trino-style hard isolation: a shared SPI, a 
loader, and one
   directory per plugin.
   
   **Architecture**
   
   ```
   be/lib/java/
     spi/       doris-jni-spi.jar        zero dependencies, the only classes BE 
and plugins share
                doris-jni-bootstrap.jar  the loader and the registry BE calls
     plugins/
       paimon/  hudi/  iceberg/  jdbc/  max-compute/  trino-connector/  
java-udf/  java-writer/
   ```
   
   A plugin's classloader has the **platform** loader as its parent and 
delegates only the
   `org.apache.doris.jni.spi.` prefix. Nothing else is shared - each plugin 
carries its own
   hadoop, its own filesystem implementations, its own logging bridges. BE 
addresses a plugin
   by `(plugin name, factory name)` instead of by class name, so the wiring is 
one table in
   `be/src/util/jni_plugin_registry.h` rather than string literals spread over 
the readers.
   
   The system classpath is now `conf/` + `lib/java/spi` + `lib/hadoop_hdfs` + 
JindoFS/JuiceFS.
   `preload-extensions` and `java-common` are gone.
   
   **Also in this PR**
   
   - **The JVM bootstrap moves into Doris** 
(`be/src/util/jvm_launcher.{h,cpp}`). It used to
     belong to libhdfs, which meant BE's JNI ability depended on libhdfs being 
linked. The
     three-step bootstrap libhdfs performed is reproduced exactly, including
     `FileSystem.loadFileSystems()` - hadoop records its providers once, under 
the first
     caller's TCCL, so skipping it defers that scan to whichever plugin thread 
gets there
     first. The JVM is now created lazily: no Java query and no HDFS access 
means no JVM.
   - **A plugin failing to load is a plugin-level FAILED state**, not `exit(1)`.
   - **User Java UDFs see only the contract** - `org.joda.time.` and the Hive 
UDF base classes -
     and nothing else Doris happens to have on its classpath.
   
   **Sizes** (fat jar -> plugin directory): jdbc 5.6MB -> 3.8MB, java-udf 80MB 
-> 20MB,
   trino-connector 110MB -> 73MB, max-compute 67MB -> 60MB.
   
   **Behaviour changes worth calling out**
   
   - `log/jni.log` becomes `log/jni.log.0` (JUL rotation naming, 10MB x 5).
   - `log/trinoconnector0.log` is removed. Its FileHandler was attached to the 
JUL **root**
     logger, which is process-wide, so after isolation it would collect every 
plugin's log.
     Trino logs through `io.airlift.log.Logger` (JUL) and lands in `jni.log` 
anyway.
   - The `expiration_time` UDF property is not carried into the new module. It 
has been
     ignored since time-based eviction was removed; it stays accepted and stays 
ineffective.
   - Profile node names use the plugin name (`paimon`) instead of the class name
     (`PaimonJniScanner`).
   - Under hudi, `file://` now resolves to hive-apache's `ProxyLocalFileSystem` 
rather than
     hadoop's `LocalFileSystem` (a subclass overriding only `rename`). 
Previously the static
     registry was filled once by BE's TCCL; now each plugin fills its own.
   
   **New CI check**
   
   `tools/be-java-plugins/check_plugin_layout.py` runs against a built 
`output/be/lib/java`
   and enforces four rules: the SPI jar carries only SPI packages; no plugin 
ships an SPI
   class; no class inside a plugin directory resolves to different bytes in 
different jars;
   and every class referenced by Doris's own code in a plugin resolves inside 
that directory.
   The known-benign exceptions are allowlisted with the reason written down. 
The file states
   what it does **not** prove: it is static and starts from Doris's classes, so 
anything
   reached by ServiceLoader or reflection is invisible to it.
   
   ### Release note
   
   None
   
   ### Check List (For Author)
   
   - Test
       - [x] Unit Test
       - [x] Manual test (add detailed scripts or steps below)
   
   Per-module unit tests (716 tests across the 22-module reactor, 0 failures / 
0 errors /
   0 skipped), plus for each plugin an end-to-end test that builds a real table 
and reads it
   back through the plugin factory, and a **deployment-directory load probe**: 
the plugin is
   loaded out of the directory `build.sh` produces, using the real 
`PluginRegistry`.
   
   That last one is not redundant. Surefire puts `provided` dependencies on the 
test
   classpath, so a dependency wrongly marked `provided` leaves every unit test 
green and only
   fails when the plugin is loaded for real - verified three times during this 
work by
   mutation (hadoop-common under iceberg, hadoop-mapreduce-client-core under 
hudi, guava
   under trino-connector).
   
   Each step was mutation tested; the mutations and which assertion each one 
killed are
   recorded per commit.
   
   - Behavior changed:
       - [x] Yes. See "Behaviour changes worth calling out" above.
   
   - Does this need documentation?
       - [x] Yes. Deployment layout, the `jni.log.0` name, and the removal of
         `trinoconnector0.log` are user-visible. Doc PR to follow.
   
   ### State of this PR
   
   Not ready to merge. Outstanding:
   
   - **Rebase.** The branch is based on 5378480d492 and conflicts with #66564, 
which touches
     `build.sh`, `hadoop-deps/pom.xml` and `preload-extensions/pom.xml` - the 
last of which
     this PR deletes. There is also a semantic overlap: #66564 makes
     `--exclude-obs-dependencies` prevent Huawei artifacts from resolving at 
all, while the
     plugins here declare `hadoop-huaweicloud` unconditionally, so they need 
the same profile
     treatment.
   - **Full regression run.** The per-connector suites have not been run 
against a deployed
     cluster yet.
   - **Three end-to-end assertions** that need a real BE: no libjvm mapping in 
`/proc` when no
     plugin is deployed; a query against an undeployed plugin reports `is not 
deployed`; a
     corrupt jar does not stop BE from starting.
   - **One open experiment.** jindo-core carries a native library, and a JVM 
binds one of those
     to exactly one classloader. A BE reading `oss-hdfs://` natively through 
libhdfs and a
     plugin loading its own copy are mutually exclusive; this needs measuring 
on a real
     deployment.
   


-- 
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]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to