Skip to content

test: enable the Iceberg split-operator and native write by default to surface test failures - #5677

Draft
andygrove wants to merge 2 commits into
apache:mainfrom
andygrove:iceberg-write-defaults-experiment
Draft

test: enable the Iceberg split-operator and native write by default to surface test failures#5677
andygrove wants to merge 2 commits into
apache:mainfrom
andygrove:iceberg-write-defaults-experiment

Conversation

@andygrove

@andygrove andygrove commented Sep 4, 2026

Copy link
Copy Markdown
Member

Which issue does this PR close?

None, this is exploratory. Related to #5308 (native Iceberg write) and the split-operator work in #4658.

Draft, not for merge. The point is to find out what breaks.

Rationale for this change

spark.comet.iceberg.write.enabled has defaulted to false since it landed, so the iceberg-rust writer has
only ever run in the handful of Comet suites that set it explicitly: CometIcebergWriteActionSuite,
CometIcebergWriteDetectionSuite, CometIcebergRewriteActionSuite, and CometIcebergSystemFunctionSuite.
Everything else, including the Iceberg Spark SQL suites that run Iceberg's own tests against Comet, has been
writing through Spark's stock executor-side writer. So we do not currently know how much of the Iceberg test
surface the native writer actually passes.

spark.comet.write.iceberg.splitOperator.enabled is a different story. The Iceberg test diffs have set it to
true in every Comet-configured session since #5259, so the two-operator plan has been running against
Iceberg's write, commit, and row-level-operation tests all along. Only the native writer is genuinely new
here, and flipping the user-facing default keeps the two consistent.

Turning the native write flag on is not sufficient on its own. CometIcebergNativeWrite sets
requiresNativeChildren, and Spark emits a row-based LocalTableScanExec for an inline VALUES list, so a
write fed that way is declined and silently runs on the JVM writer. Iceberg's suites seed data with
INSERT INTO ... VALUES and df.writeTo(...).append() constantly, and nothing in those suites asserts which
writer produced the files, so without spark.comet.exec.localTableScan.enabled the native writer would stay
hidden from a large part of the write surface while everything still passed green.

Measured locally on Spark 4.1.3 with the Iceberg 1.11 runtime, using the session config the Iceberg diffs
install:

Write shape Native write on Native write plus local table scan
INSERT INTO ... VALUES, unpartitioned JVM writer native writer
df.writeTo(...).append(), unpartitioned JVM writer native writer

Writes that already had a Comet-native child reached the native writer either way: partitioned
INSERT INTO ... VALUES, INSERT INTO ... SELECT, copy-on-write UPDATE, CTAS, and a target carrying
write.parquet.compression-codec.

What changes are included in this PR?

  • CometConf.scala: default spark.comet.write.iceberg.splitOperator.enabled and
    spark.comet.iceberg.write.enabled to true, and drop the now false "Off by default" sentence from the
    native write config doc.
  • dev/diffs/iceberg/{1.8.1,1.9.1,1.10.0,1.11.0}.diff: set spark.comet.exec.localTableScan.enabled=true
    alongside the existing Comet configuration in every Comet-configured session, so VALUES driven and
    DataFrame driven writes can reach the native writer instead of silently falling back. Each diff was
    regenerated against its own tag following docs/source/contributor-guide/iceberg-spark-tests.md.
  • docs/source/contributor-guide/iceberg-spark-tests.md: document that flag and the existing
    spark.comet.explainFallback.enabled setting, and drop the stale claim that the split-operator flag is off
    by default.

Deliberately not included: docs/source/user-guide/latest/iceberg-writes.md still says both features are
experimental and off by default, and still lists splitOperator.enabled being false as the first fallback
reason. If any of this is worth keeping, the guide has to be rewritten, but that is premature until we see the
failures.

How are these changes tested?

By CI, which is the whole point. The run-iceberg-tests label is applied so the Iceberg 1.8 / 1.9 / 1.10 jobs
run alongside 1.11, giving coverage across Spark 3.4 / 3.5 / 4.1 and four Iceberg versions. Comet's own
Iceberg suites run in the [scans] bucket on every profile regardless.

The Iceberg diffs also set spark.comet.explainFallback.enabled=true, so Comet logs every operator it
declines together with the reason. That output is not usable from CI as things stand: gradle does not forward
Spark's log output into the job log, and a 130k line Iceberg job log contains zero WARN lines. Reading fallback
rates needs either a gradle test-logging change or a local run. Until then, the pass or fail result is all CI
gives us, and it does not distinguish a native write from a silent fallback.

The regenerated diffs were checked to apply cleanly against their tags and to produce one
localTableScan.enabled line per existing splitOperator.enabled line. The measurement in the table above
came from a throwaway probe suite built on the same session config the diffs install.

What the run found

All eight Iceberg jobs fail. The failures are dominated by one bug, filed as #5689: converting
IcebergWriteExec to CometIcebergWriteExec drops the ColumnarToRow that sat between Iceberg's columnar
BatchScan and the whole-stage codegen stage above it, so every copy-on-write DELETE, UPDATE and MERGE dies
with ColumnarBatch cannot be cast to InternalRow. It only shows up when AQE is disabled, which is why no
existing Comet suite caught it. Isolated locally: the split-operator plan alone does not trigger it and the
local table scan flag is irrelevant, it needs the native write flag plus AQE off.

Beware of reading the extension-suite failure counts as a trend. Iceberg's ExtensionsTestBase sets
ADAPTIVE_EXECUTION_ENABLED to RANDOM.nextBoolean() per session, so the number of tests #5689 takes down
swings between runs of the same behaviour (99 in one run, 31 in the next).

Also found:

In the [scans] jobs, several CometIcebergWriteActionSuite cases fail with
expected >= 1 IcebergWriteExec in captured plans, got 0. Those tests assert that the JVM split writer is
present in the plan, and the native writer now takes over instead. Their premise has to be revisited if these
defaults ever ship.

Both toggles have defaulted to false since they landed, so no CI job has ever
exercised the two-operator plan or the iceberg-rust writer outside the handful of
suites that set them explicitly. Flip the defaults to find out what the Iceberg
Spark SQL suites and Comet's own Iceberg suites say when every write goes through
them.

Exploratory: `iceberg-writes.md` still documents both as off by default and is
deliberately not updated here.
The Iceberg test sessions already configure the write split operator, but
they leave `spark.comet.exec.localTableScan.enabled` off. Spark emits a
row-based `LocalTableScanExec` for an inline `VALUES` list, and
`CometIcebergNativeWrite` sets `requiresNativeChildren`, so those writes are
declined and silently run on the JVM writer. Iceberg's suites seed data that
way constantly, so the native writer stayed hidden from most of the write
surface even with `spark.comet.iceberg.write.enabled` on.

Measured on Spark 4.1.3 with the Iceberg 1.11 runtime and the Iceberg CI
session config: unpartitioned `INSERT INTO ... VALUES` and
`df.writeTo(...).append()` both selected the JVM writer before this change
and the native writer after it.

Regenerated all four diffs against their own tags per the contributor guide.
Also document the flag and the existing `spark.comet.explainFallback.enabled`
setting, which reports every declined operator with its reason, and drop the
stale claim that the split-operator flag is off by default.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant