Skip to content

[836] xtable-spark-runtime: thin drop-in bundle (packaging) + resilient target discovery#838

Draft
vinishjail97 wants to merge 4 commits into
apache:mainfrom
vinishjail97:836-spark-runtime-packaging
Draft

[836] xtable-spark-runtime: thin drop-in bundle (packaging) + resilient target discovery#838
vinishjail97 wants to merge 4 commits into
apache:mainfrom
vinishjail97:836-spark-runtime-packaging

Conversation

@vinishjail97

@vinishjail97 vinishjail97 commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

What is the purpose of the pull request

First of two PRs splitting #837 (per review feedback) into packaging and listener. This is the packaging PR (the listener stacks on top): it introduces the xtable-spark-runtime module producing a thin, drop-in Spark bundle, plus RFC-3, and validates the jar end-to-end via spark-submit.

Brief change log

  • RFC-3 (rfc/rfc-3/rfc-3.md).
  • New module xtable-spark-runtime_${scala.binary.version} (thin shaded bundle, ~3.6 MB).
  • XTableSparkSync — standalone spark-submit entry point (RunSync-equivalent for this bundle).
  • TableSyncSpec / XTableSyncService — shared sync building blocks (reused by the listener PR).
  • ConversionTargetFactory: make ServiceLoader target discovery resilient — skip targets whose engine lib isn't on the classpath (separable core change; see commit 1).
  • ITXTableSparkRuntimeBundle — spark-submit jar-validation test.

Packaging model (the important part)

Per review feedback, engines are not bundled. Spark, Hadoop, and Hudi/Iceberg/Delta are all provided — the user brings their own engine versions (Hudi 1.1 / 1.2 / …) and the thin XTable jar stays compatible across them.

  • Curated shade allowlist: only XTable modules + guava/protobuf (relocated under org.apache.xtable.shaded.*).
  • Libraries XTable exchanges with the engines are deliberately not relocated/bundled — relocating avro breaks the boundary (Hudi returns a real org.apache.avro.SchemaNoSuchMethodError). avro/parquet/jackson/commons come from the provided runtime.
  • Because target discovery uses ServiceLoader, it's made resilient so a user can run with only the engines they use (e.g. Hudi + Iceberg, no Delta).

Verify this pull request

ITXTableSparkRuntimeBundle (skipped unless SPARK_HOME is set) builds the shaded jar and runs a real sync:

SPARK_LOCAL_IP=127.0.0.1 mvn -pl xtable-spark-runtime -am verify

It creates a local Hudi table, spark-submits the bundle with Hudi + Iceberg supplied via --packages (versions from the root pom: Hudi 1.2.0, Iceberg 1.9.2), and asserts the Iceberg metadata is produced. Validated locally on Spark 3.5.2 (engines built for 3.4 → also a cross-version check).

Part of #836. Listener PR follows, stacked on this branch.

🤖 Generated with Claude Code

vinishjail97 and others added 2 commits July 7, 2026 13:21
ConversionTargetFactory loads targets via ServiceLoader and eagerly
instantiates every registered target when resolving any one of them.
With a thin runtime where engines are provided, that hard-fails (e.g.
DeltaKernelConversionTarget needs io.delta.kernel) even for a sync that
never touches Delta.

Skip providers whose engine library is not on the classpath so a subset
of engines can be used (e.g. Hudi + Iceberg, no Delta). Also make the
Delta-kernel check name-based to avoid loading DeltaKernelConversionTarget
(and its io.delta.kernel dependencies) when Delta is absent.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
New xtable-spark-runtime module producing a thin, drop-in bundle for
in-job XTable metadata sync, plus RFC-3.

Packaging (the Hudi/Iceberg model):
- Spark, Hadoop, and the engine libs (Hudi/Iceberg/Delta) are provided;
  the user brings their own engine versions and the bundle stays
  compatible across them.
- Curated shade allowlist: only XTable's own modules + guava/protobuf
  (relocated under org.apache.xtable.shaded.*). Libraries exchanged with
  the engines (avro/parquet/jackson/commons) are NOT relocated/bundled -
  relocating avro breaks the boundary (Hudi returns a real avro Schema).
- Resulting bundle: ~4 MB.

Includes XTableSparkSync, a standalone spark-submit entry point (the
RunSync-equivalent for this bundle), and TableSyncSpec/XTableSyncService
shared with the listener (stacked follow-up PR).

Validation: ITXTableSparkRuntimeBundle spark-submits the shaded jar via
$SPARK_HOME with Hudi + Iceberg supplied through --packages (versions from
the root pom) and asserts the Iceberg metadata is produced. Skipped when
SPARK_HOME is unset.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
private static final String DELTA_KERNEL_TARGET_CLASS =
"org.apache.xtable.kernel.DeltaKernelConversionTarget";

// Name-based to avoid loading DeltaKernelConversionTarget (and its io.delta.kernel dependencies)

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.

Remove this comment..the code is self explanatory.

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.

Removed. Done in 96da5d6.

// A registered target whose engine library is not on the classpath (e.g. Delta when only
// Hudi/Iceberg are provided). Skip it so a subset of engines can still be used; a missing
// engine for the requested format surfaces below as NotSupportedException.
log.debug(

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.

Make this a warn log so that it's visible for the user if some provided package is missing?

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.

Changed to log.warn with a message pointing the user to provide the missing engine, so an absent provided package is visible. Done in 96da5d6.

}
}

private static Map<String, String> parseArgs(String[] args) {

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.

Is this the right to parse? Any existing libraries that can be used to avoid hand-written logic?
https://github.com/apache/incubator-xtable/blob/main/xtable-utilities/src/main/java/org/apache/xtable/utilities/RunSync.java#L240

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 call. Switched to Apache Commons CLI (Options + DefaultParser + HelpFormatter), mirroring RunSync. commons-cli is bundled and relocated under org.apache.xtable.shaded.*. Done in 96da5d6.

…al bundle IT

Review feedback on PR apache#838:
- XTableSparkSync now parses args with Apache Commons CLI (mirrors RunSync)
  instead of hand-rolled parsing; the relocated commons-cli is bundled.
- ConversionTargetFactory logs a warn (was debug) when it skips a registered
  target whose engine library is absent, so a missing provided engine is
  visible; dropped the self-explanatory comment on isDeltaKernelTarget.

Bundle integration test (ITXTableSparkRuntimeBundle):
- Supplies engines on a flat classpath via spark.driver.extraClassPath, and
  adds Avro 1.12 + parquet-avro to the engine classpath: Iceberg 1.9.2 (bumped
  in apache#784) compiles against Avro 1.12 APIs, which Spark 3.5's Avro 1.11.2 lacks.
  A flat classpath keeps one Avro on one loader, unlike --packages layering.
- Adds jol-core (provided) that Hudi needs at runtime.
- XTableSyncService writes target metadata at the source data path (required by
  Hudi; Iceberg data lives under <basePath>/data); IT passes --dataPath.
- Readback mirrors ITConversionController: target read at the data path, Hudi
  metadata read options, per-format timestamp normalization, scalar-column
  data-equivalence, and non-vectorized Iceberg reads.
- Relocates guava's com.google.thirdparty with the rest of guava.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.

1 participant