tchivs opened a new pull request, #8292:
URL: https://github.com/apache/paimon/pull/8292
# Draft PR Plan
## Title
Introduce a file format provider boundary for no-Hadoop engines
## Motivation
Paimon already has a `FileIO` abstraction, but current ORC and Parquet
format construction still requires Hadoop-facing runtime classes in some paths.
This is a problem for engines such as Trino that provide native object-store IO
and native ORC/Parquet implementations, and do not expose Hadoop classes from
the connector runtime.
An earlier bundle-packaging experiment proved the runtime failures can be
hidden by carrying Hadoop classes inside the Paimon bundle, but that is a
packaging fallback rather than the target architecture. Paimon should be able
to support engines with native format implementations without requiring them to
ship Hadoop runtime classes.
## Scope
This first patch adds a small experimental provider boundary:
- `FileFormatProvider` lets an embedding engine supply a `FileFormat` for an
identifier such as `orc` or `parquet`.
- `FileFormat.fromIdentifier` uses a provider only when
`file.format.provider` explicitly selects one.
- `FileFormat.readerFromIdentifier`, `writerFromIdentifier`, and
`validationFromIdentifier` allow engines to select providers per operation.
- `file.format.provider` remains a backward-compatible global fallback.
- `file.format.read-provider`, `file.format.write-provider`, and
`file.format.validation-provider` let an engine avoid sending read paths
through a write-only native provider.
- If no provider is selected, or the selected provider returns
`Optional.empty()` for a format identifier, Paimon falls back to the existing
service-loaded `FileFormatFactory`.
- Duplicate provider identifiers fail with a clear and deterministically
ordered `FactoryException` instead of relying on classpath order.
- Catalog-level `table-runtime.*` options let embedding engines pass
runtime-only table options such as provider selection without persisting them
in table schemas.
- Runtime table options are used when creating tables and when loading
existing tables, so validation and write paths can consistently select the
provider boundary.
- Branch table loading and `FileStoreTable.switchToBranch` preserve catalog
runtime options, so branch operations keep the same provider boundary without
persisting engine-specific options or inheriting arbitrary user dynamic options.
The patch intentionally does not port Trino's native ORC/Parquet writers
into Paimon. It creates the reviewable boundary needed for that follow-up.
## Compatibility
- Existing Hadoop-backed ORC/Parquet implementations remain the default.
- Existing file format identifiers and table options are unchanged.
- Engines that do not explicitly select a `FileFormatProvider` should see no
behavior change.
- The new provider contract uses Paimon-owned types (`FileFormat`,
`FormatContext`, `Options`, `RowType`, `FormatReaderFactory`,
`FormatWriterFactory`) and does not expose Trino classes.
- Operation-specific provider keys let an engine provide only the operations
it owns; for example Trino can provide validation and writes while leaving
reads on Paimon's default reader discovery.
- Runtime options are not serialized into table schema files, so they do not
make table metadata engine-specific.
## Test Evidence
Provider behavior:
- explicit provider selection under a no-Hadoop classloader;
- provider is not used without `file.format.provider`;
- provider can return `Optional.empty()` to fall back to the default
`FileFormatFactory`;
- duplicate provider identifiers fail clearly with deterministically ordered
ambiguous class names.
- read/write/validation provider keys do not affect each other.
- catalog `table-runtime.*` options participate in table validation and
runtime table loading without being persisted in schema files.
- file-format and provider identifiers are normalized with `Locale.ROOT`,
including uppercase
format identifiers and JVM-default-locale edge cases.
- fallback branch switching preserves catalog runtime provider options,
keeps `table-runtime.path`
from replacing the table location or `options().path`, and does not
inherit unrelated
`copy(...)` dynamic options.
Focused provider tests:
```bash
mvn -pl paimon-common,paimon-core,paimon-format -am -Pfast-build
-DfailIfNoTests=false
-Dtest=FileFormatProviderTest,SchemaValidationTest,SchemaManagerTest,FileSystemCatalogTest,FormatProviderNoHadoopTest
test
```
Broader format regression:
```bash
mvn -pl paimon-format -am -Pfast-build -DfailIfNoTests=false
-Dtest=FormatProviderNoHadoopTest,ParquetReadWriteTest,OrcBulkWriterTest test
```
This passed with 45 tests in the latest local refresh.
Bundle packaging:
```bash
mvn -pl paimon-bundle -am -Pfast-build -DskipTests install
```
Fallback branch regression:
```bash
mvn -pl paimon-core -am -Pfast-build -DfailIfNoTests=false
-Dtest=FallbackReadFileStoreTableTest test
```
License / whitespace:
```bash
git diff --check
git diff --check apache/master
mvn -N org.apache.rat:apache-rat-plugin:0.15:check
```
Current final verification:
```bash
mvn -pl paimon-common,paimon-core -am -Pfast-build -DfailIfNoTests=false
-Dtest=FileFormatProviderTest,FallbackReadFileStoreTableTest#testSwitchToBranch,FileSystemCatalogTest#testCreateTableWithRuntimeCatalogOptions,SchemaValidationTest#testSnapshotSequenceOrderingHonorsDynamicWriteOnlyValue+testDynamicOptionsCanRemoveSchemaOptionsDuringValidation,SchemaManagerTest#testCreateTableWithDynamicOptions+testCommitChangesWithDynamicOptions,FileMetaUtilsTest
test
mvn -pl paimon-format -am -Pfast-build -DfailIfNoTests=false
-Dtest=FormatProviderNoHadoopTest,ParquetReadWriteTest,OrcBulkWriterTest test
mvn -pl paimon-bundle -am -Pfast-build -DskipTests install
git diff --check
git diff --check apache/master
mvn -N org.apache.rat:apache-rat-plugin:0.15:check
```
The current Paimon hardening-focused command passed with 18 tests (11 in
`paimon-common`
and 7 in `paimon-core`), the format regression passed with 45 tests, and the
bundle install
refreshed the local `1.5-SNAPSHOT` artifact consumed by the Trino companion
branch.
Latest pre-commit review refresh added explicit `path` assertions for branch
and fallback
runtime-option isolation. A full PR-diff audit also fixed a missing `@Test`
annotation on
`SchemaValidationTest#testSnapshotSequenceOrderingHonorsDynamicWriteOnlyValue`,
so dynamic
`write-only` validation is now actually executed by Surefire. Current `git
diff --check`,
`git diff --check apache/master`, and RAT all pass.
The bundle install was refreshed after the latest runtime-path hardening.
Target and installed
Maven bundle inspection both report `paimon-bundle-1.5-SNAPSHOT.jar` at
`48,998,103` bytes
(`46.7 MiB`), with `0` ordinary Hadoop entries, `0` Hadoop runtime-prefix
entries, `0` Hadoop
`Configuration.class`, `0` `FileInputFormat.class`, and `1`
`FileFormatProvider.class`.
## Package Footprint
- `paimon-format-1.5-SNAPSHOT.jar` is `26,357,158` bytes (`25.1 MiB`) in the
latest local build.
- `paimon-bundle-1.5-SNAPSHOT.jar` is `48,998,103` bytes (`46.7 MiB`) in the
latest local build.
- The branch does not add embedded Hadoop core/runtime classes.
- Ordinary `org/apache/hadoop/` entries are `0`.
- Hadoop core/runtime classes in Paimon's dependency-isolation namespace are
`0`.
- Hadoop `Configuration.class` entries are `0`.
- Paimon's existing dependency-isolation namespace remains for non-Hadoop
format libraries, and
package checks verify that it contains no Hadoop core/runtime bytecode.
- Existing Hive storage API classes remain under Paimon's internal
dependency namespace; they are not Hadoop core/runtime classes.
## Runtime Option Boundaries
Catalog-level `table-runtime.*` options are runtime-only. Focused FileSystem
catalog coverage now
verifies that provider options are visible on loaded FileStore tables, while
`table-runtime.path`
does not redirect the loaded table away from the catalog location and is not
persisted to schema
files. The validation-provider test records the provider `FormatContext`
path and verifies it sees
the persisted schema path, never the catalog `table-runtime.path` value.
Branch table coverage
verifies the same runtime provider option is preserved when loading a
catalog branch table, while
runtime provider and path options remain absent from the branch schema file.
Fallback branch-switch
coverage additionally verifies that `switchToBranch` recovers catalog
runtime options from
`CatalogEnvironment.catalogContext()` rather than inheriting arbitrary user
dynamic options, and
that the switched table keeps the real table path in both `location()` and
`options()`.
## Companion Validation
A local Trino companion branch demonstrates the provider boundary with Trino
native ORC/Parquet writers:
- `table-runtime.file.format.validation-provider=trino` is set at catalog
creation time for schema validation;
- `file.format.write-provider=trino` is added only on supported write paths;
- normal reads do not use Trino's write-only provider;
- earlier focused connector suite passes (`370` tests);
- runtime dependency tree has no `org.apache.hadoop`, `io.trino.hadoop`, or
`org.apache.parquet:parquet-hadoop` entries;
- packaged Trino Paimon plugin has no standalone Hadoop or `parquet-hadoop`
jars;
- embedded Paimon bundle remains about `47M` with `0` ordinary Hadoop
entries and `0` Hadoop core/runtime classes under Paimon's internal dependency
namespace.
- the latest refreshed Trino plugin zip also reports
`connector_provider_class=1`,
`connector_provider_service_entries=1`, and
`connector_stats_extractor_class=1`
(`io.trino.plugin.paimon.format.TrinoPaimonSimpleStatsExtractor.class`).
- the latest post-hardening refresh passed the SQL provider regression,
runtime dependency tree,
plugin package, and zip inspection against the refreshed local Paimon
bundle.
- the current Trino plugin zip file size is `278,966,092` bytes (`266.0
MiB`); compressed ZIP
entries total `278,908,958` bytes and uncompressed entries total
`301,569,934` bytes. It contains
no standalone Hadoop, Trino Hadoop, or `parquet-hadoop` jars, embeds a
`48,998,103` byte
(`46.7 MiB`) Paimon bundle with no Hadoop runtime classes, and contains
the provider service,
provider class, and stats extractor class inside `trino-paimon-440.jar`.
Filesystem `du` may
report `512M` disk usage because of block allocation; use the ZIP file
size (`278,966,092` bytes)
for PR evidence.
Current companion commands:
```bash
JAVA_HOME=/root/.jdks/temurin-21.0.11
PATH=/root/.jdks/temurin-21.0.11/bin:$PATH ./mvnw -pl plugin/trino-paimon -nsu
-Dtest=TrinoITCase#testSqlCreateInsertReadUsesTrinoProviderForParquetAndOrc test
JAVA_HOME=/root/.jdks/temurin-21.0.11
PATH=/root/.jdks/temurin-21.0.11/bin:$PATH ./mvnw -pl plugin/trino-paimon -nsu
-DskipTests dependency:tree -Dscope=runtime
-Dincludes=org.apache.hadoop,io.trino.hadoop,org.apache.parquet:parquet-hadoop
JAVA_HOME=/root/.jdks/temurin-21.0.11
PATH=/root/.jdks/temurin-21.0.11/bin:$PATH ./mvnw -pl plugin/trino-paimon -nsu
-DskipTests package
```
The SQL provider regression passed with 1 test, the runtime dependency tree
emitted no matching
Hadoop or `parquet-hadoop` dependencies, and the packaged zip inspection
passed the no-Hadoop
artifact checks above.
## Follow-Ups
1. Decide whether `FileFormatProvider` and `table-runtime.*` should stay
`@Experimental` for one release or be promoted into documented extension points.
2. Decide whether `file.format.provider` should remain a runtime-only engine
selector or become a documented catalog/table option.
3. Move more ORC/Parquet implementation details behind no-Hadoop
reader/writer contracts once the boundary is accepted.
--
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]