Skip to content

[836] xtable-spark-runtime: thin drop-in Spark bundle (Hudi 0.x)#843

Open
vinishjail97 wants to merge 6 commits into
apache:branch-0.4from
vinishjail97:836-spark-runtime-hudi0x
Open

[836] xtable-spark-runtime: thin drop-in Spark bundle (Hudi 0.x)#843
vinishjail97 wants to merge 6 commits into
apache:branch-0.4from
vinishjail97:836-spark-runtime-hudi0x

Conversation

@vinishjail97

Copy link
Copy Markdown
Contributor

Draft for review — ports xtable-spark-runtime onto the Hudi 0.x line (main-hudi-0x, Hudi 0.14 / Spark 3.4). A thin, relocated Spark bundle that runs an incremental XTable sync in-job; engines are provided by the cluster, never bundled.

Why (context)

Per the [DISCUSS] rc1 for 0.4.0 thread, 0.4.0 is cut from the Hudi 0.x line and xtable-spark-runtime is a headline on-ramp (a better path than the standalone utilities jar). This is the Hudi-0.x counterpart of the packaging PR #838 (which targets main / Hudi 1.x).

Module

  • xtable-spark-runtime_${scala.binary.version}xtable-core compile; Spark/Hadoop + engines provided. Curated shade allowlist (xtable modules + guava/protobuf/commons-cli relocated); avro/parquet/jackson not relocated (exchanged with the engines). Thin ~3.7 MB bundle.
  • XTableSparkSync — standalone spark-submit entry point (Apache Commons CLI).
  • XTableSyncService / TableSyncSpec — build an INCREMENTAL ConversionConfig and run ConversionController.sync; the target metadata path is the source data path (required by Hudi; Iceberg data lives under <basePath>/data).
  • ConversionTargetFactory — resilient ServiceLoader discovery so a subset of engines works when others are absent (warn + skip on LinkageError/ServiceConfigurationError; name-based Delta-Kernel check).

Hudi 0.14 specifics (vs the Hudi 1.x variant on main)

  • No hudi-hadoop-common (that split is 1.x); hudi-common still provides FSUtils.
  • The engine classpath uses the hudi-spark bundle — its regenerated Avro model classes (e.g. DecimalWrapper) link on Avro 1.12 (required by Iceberg 1.9.2), whereas raw hudi-common's are Avro-1.8/1.9-generated and reference TimeConversions.TimeConversion, removed in Avro ≥1.10. Plus hudi-java-client for the Hudi target's Java write client, with raw hudi-common excluded so the bundle's clean class wins.

Testing

ITXTableSparkRuntimeBundle spark-submits the shaded jar for one case per direction across Hudi, Iceberg and Delta (source and target), engines on a flat spark.driver.extraClassPath, asserting data-equivalence over comparable scalar columns (local-timestamp columns excluded — HUDI-7088). Verified locally green (4/4) on a Spark 3.4.3 SPARK_HOME. The IT is SPARK_HOME-gated, so CI skips it.

🤖 Generated with Claude Code

Ports the xtable-spark-runtime packaging slice onto the Hudi 0.x line
(main-hudi-0x, Hudi 0.14 / Spark 3.4). New thin, relocated bundle that
runs an incremental XTable sync inside a Spark job; engines are provided
by the cluster, never bundled.

Module:
- xtable-spark-runtime_${scala.binary.version}: xtable-core compile;
  Spark/Hadoop and the engines (Hudi/Iceberg/Delta) provided. Curated
  shade allowlist (xtable modules + guava/protobuf/commons-cli relocated);
  avro/parquet/jackson NOT relocated (exchanged with the engines). Thin
  ~3.7 MB bundle.
- XTableSparkSync: standalone spark-submit entry point (Apache Commons CLI).
- XTableSyncService / TableSyncSpec: build an INCREMENTAL ConversionConfig
  and run ConversionController.sync; target metadata path = source data
  path (required by Hudi; Iceberg data lives under <basePath>/data).

Hudi 0.14 specifics (vs the Hudi 1.x variant on main):
- No hudi-hadoop-common (that split is 1.x); hudi-common provides FSUtils.
- Engine classpath uses the hudi-spark bundle (its regenerated Avro model
  classes link on Avro 1.12, which Iceberg 1.9.2 requires) plus
  hudi-java-client for the Hudi target's Java write client, with the raw
  hudi-common excluded so the bundle's clean DecimalWrapper wins.

ConversionTargetFactory: make ServiceLoader discovery resilient so a
subset of engines works when others are absent (warn + skip on
LinkageError/ServiceConfigurationError; name-based Delta-Kernel check).

ITXTableSparkRuntimeBundle: spark-submits the shaded jar for one case per
direction across Hudi, Iceberg and Delta (source and target), engines on
a flat classpath, asserting data-equivalence over comparable scalar
columns. Requires a Spark 3.4 SPARK_HOME; skipped otherwise.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Comment thread xtable-spark-runtime/pom.xml Outdated
Comment thread rfc/rfc-3/rfc-3.md
Comment thread rfc/rfc-3/rfc-3.md
Comment thread xtable-spark-runtime/pom.xml
Comment thread xtable-spark-runtime/pom.xml
Comment thread xtable-spark-runtime/pom.xml
Comment thread xtable-spark-runtime/src/main/java/org/apache/xtable/spark/XTableSyncService.java Outdated
vinishjail97 and others added 3 commits July 9, 2026 18:20
…sing, RFC)

Publish the shaded jar as the MAIN artifact with a dependency-reduced POM
(shadedArtifactAttached=false, createDependencyReducedPom=true), matching
iceberg-spark-runtime / hudi-spark-bundle. Resolving the coordinate via a
Maven dependency or --packages now yields the relocated bundle and pulls no
un-relocated transitive deps; --jars is equivalent. The thin main jar +
separate -bundle classifier (which re-introduced the cluster guava clash)
is gone. IT findBundleJar() now picks the shaded main jar.

Pass release/scripts/validate_shaded_license_coverage.sh (the existing
allowlist gate): the shade <includes> must equal the runtime dependency
tree, so jackson / scala-library / log4j-1.2-api - which every Spark
runtime supplies - are declared provided (dropped from the tree, kept off
the shaded jar) and excluded from the IT's flat engine classpath so Spark's
own copies win. avro/parquet stay on the flat classpath (the engine's newer
avro must win over Spark 3.4's).

Bundled-dependency licensing: add META-INF/LICENSE-bundled and
NOTICE-bundled (wired via IncludeResourceTransformer) attributing the only
bundled third-party - guava's closure (Apache-2.0) and commons-cli
(Apache-2.0), plus checker-qual (MIT). Remove the dead protobuf-java
allowlist entry and relocation: protobuf resolves as provided and was never
bundled.

RFC-3: scope v1 to the CLI (XTableSparkSync); mark the config-only
XTableSyncListener on-ramp (and XTableSparkConfig / PlanTargetResolver) as a
deferred follow-up. Fix the activation example to the shipped CLI and
document the required engine Avro version + flat-classpath placement.

XTableSyncService: normalize sourceFormat with Locale.ROOT.

Root pom: exclude the shade-generated dependency-reduced-pom.xml from
spotless (no license header; CI runs clean install).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Consolidate repeated rationale in the bundle pom (no functional change):
- Tell the hudi-common DecimalWrapper / Avro-1.8-1.9 story once (on the
  hudi-spark bundle dep); the hudi-java-client exclusion just points to it.
- State "engine Avro must win on a flat classpath" once (engine-classpath
  plugin); the avro dep and excludeGroupIds comments reference it.
- Explain jackson/scala-library are Spark-supplied once.
- Fix a stale "Spark 3.5" reference to Spark 3.4 (this is the Hudi 0.x line).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add main-hudi-0x to the push and pull_request branch filters of both
workflows (same change as apache#841) so CI runs on PRs targeting the Hudi 0.x
release line, including this one. A pull_request workflow's branch filter
takes effect from the PR's own branch, so this makes CI fire on apache#843.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@vinishjail97 vinishjail97 marked this pull request as ready for review July 10, 2026 01:29
…k-submit stdout off-thread

- ConversionTargetFactory: hasNext() resolves provider classes lazily and can
  throw ServiceConfigurationError too, so move it inside the existing
  ServiceConfigurationError|LinkageError guard alongside next().
- ITXTableSparkRuntimeBundle: drain spark-submit stdout on a background thread
  so a hung process is caught by waitFor(10min) instead of blocking forever on
  readOutput() reading until EOF.
main-hudi-0x was deleted and replaced by branch-0.4 (the 0.4.0 release line),
so update the push/pull_request branch filters accordingly.
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.

2 participants