andygrove opened a new pull request, #6066:
URL: https://github.com/apache/datafusion-comet/pull/6066
## Which issue does this PR close?
Closes #6063.
## Rationale for this change
On-heap mode is not a production configuration.
`spark.comet.exec.onHeap.enabled` defaults to
`false`, lives in `CATEGORY_TESTING`, and `CometDriverPlugin.init` disables
Comet outright when
off-heap is off and the flag is not set. Comet's own suites run off-heap
(`CometTestBase` sets
`spark.memory.offHeap.enabled=true` with 2 GiB). The only things that reach
the on-heap path are
Spark's own SQL suite and the Iceberg suites, which set
`ENABLE_COMET_ONHEAP=true` because Spark's
test harness does not configure off-heap memory.
That path nevertheless carried a complete second memory-accounting
implementation, and the
accounting it performed did not protect anything.
Comet cannot honestly join Spark's ledger in on-heap mode.
`NativeMemoryConsumer` is hardcoded to
`MemoryMode.OFF_HEAP`, and Spark sizes the off-heap execution pool from
`spark.memory.offHeap.size`
alone (`MemoryManager.scala:61-66`, byte-identical on 3.4.3, 3.5.9, 4.0.4
and 4.1.3), which is 0
when off-heap is disabled. Registering as an `ON_HEAP` consumer instead
would be a category error:
Comet's bytes are in the Rust heap, so Spark would evict cached blocks and
spill its own sorters to
make room for memory that is not on the heap, while doing nothing about the
native RSS that
actually gets an executor OOM-killed.
The fixed-size DataFusion pool that stood in for one was sized from
`spark.comet.memoryOverhead`,
which was in the initial commit (#1, February 2024), a month before
`CometTaskMemoryManager` and
the unified pool existed (#83). It survived because deleting it broke the
tests: #1062 required
off-heap and removed the on-heap branch, and #1177 restored it with the
rationale "after #1062 we
have not been running Spark tests for native execution". Per #6054, the
driver plugin could not
even fold that figure into the container on three of the five supported
Spark versions.
## What changes are included in this PR?
**Native.** `parse_memory_pool_config` returns `UnboundedMemoryPool`
whenever off-heap is disabled
and ignores the pool-type string there. Six of the nine `MemoryPoolType`
variants (`greedy`,
`fair_spill`, and their `_task_shared` and `_global` pairings) and their
arms in `mod.rs` are gone,
as is `memory_limit_per_task`, which no off-heap pool read. `createPlan`
loses the corresponding
JNI parameter.
**JVM shuffle.** `CometBoundedShuffleMemoryAllocator` becomes
`CometUnboundedShuffleMemoryAllocator`: a page table over
`UnsafeMemoryAllocator` with no budget.
The pages still have to be `Unsafe`-allocated in either memory mode, because
`SpillWriter` hands
their addresses to `writeSortedFileNative` for Rust to dereference and
`TaskMemoryManager.allocatePage` would return `long[]` heap pages here. With
no shared budget there
is nothing to wait for, so the blocking-allocation protocol added in #5493
goes away — per-thread
retention accounting, the two fail-fast liveness predicates, the timeout,
the progress logging and
the task-kill polling — and `allocateBlocking` collapses into `allocate` and
leaves
`CometShuffleMemoryAllocatorTrait`. The allocator is now created per task
like the off-heap one
rather than being an executor-wide singleton, and `getUsed` reads an
`AtomicLong` instead of taking
the allocator's monitor, since `TaskMemoryManager` calls it while holding
its own.
**Configs.** `spark.comet.memoryOverhead`,
`spark.comet.exec.onHeap.memoryPool`,
`spark.comet.shuffle.jvm.memoryFactor` and
`spark.comet.shuffle.jvm.memoryWaitTimeout` are removed,
along with `getCometMemoryOverhead*`, `getCometShuffleMemorySize` and
`shouldOverrideMemoryConf`.
`spark.comet.exec.onHeap.enabled` stays: it is still the switch that keeps
Comet off in on-heap
mode unless a test opts in, and its doc now says the mode accounts for
nothing.
**Driver plugin.** `CometDriverPlugin.init` no longer adjusts
`spark.executor.memoryOverhead`;
there is nothing left to add. Both `ShimCometDriverPlugin` files are
deleted, since
`getMemoryOverheadMinMib` was only needed by the removed calculation.
**Diffs.** The four `dev/diffs` patches drop
`.set("spark.comet.memoryOverhead", ...)`. They were
regenerated through the documented flow (clone at the tag, apply, edit, `git
diff <tag>`), not
hand-edited, and each was verified to apply cleanly to a pristine tree
afterwards. Regenerating
4.0.4 and 4.1.3 also corrects a stale `index` line for
`ParquetRowIndexSuite.scala`, whose recorded
post-image hash did not match what `git apply` actually produces. That line
is inert — CI applies
with plain `git apply` — but it is now consistent with the hunks.
## Behavior change
Nothing bounds Comet's allocations in on-heap mode any more. The on-heap
pool was the only bound
the Spark SQL suite ran under, so any memory-pressure-driven native spill it
triggers today stops
happening, and nothing caps Comet's RSS in those jobs. Given the suite's
data sizes the spill
coverage is probably near zero already, but that is an assumption rather
than a measurement.
Row-count-driven JVM shuffle spilling
(`spark.comet.shuffle.jvm.spillThreshold`) and
`spark.comet.shuffle.native.maxBufferBytes` both still trigger independently
of any pool. If CI
memory regresses, the cheap recovery is a single executor-wide
`GreedyMemoryPool` with a fixed cap,
which is one arm in `parse_memory_pool_config` rather than the whole
subsystem.
Off-heap mode is untouched.
## Overlap with #6054
#6054 is in the merge queue and also removes the driver plugin's
`spark.executor.memoryOverhead`
mutation, `shouldOverrideMemoryConf` and the shim files. This PR has to
remove them too, because it
deletes the config they consumed. I will rebase onto `main` once #6054 lands
and keep its
`warnIfExecutorMemoryOverheadUnset` addition.
## How are these changes tested?
Existing tests, adjusted:
- `CometUnboundedShuffleMemoryAllocatorSuite` (renamed) keeps the `getUsed`
accounting and
page-table-exhaustion tests, drops the two that asserted budget
exhaustion, and adds one
asserting that a 64 MiB request now succeeds.
- `CometDiskBlockWriterSuite` loses the five tests that covered the deleted
blocking protocol. The
three that cover behavior this PR keeps — write() reclaiming buffered
pages on a fatal error, the
`SpillSorter` constructor not leaking on a failed allocation, and the
unsafe writer allocating
nothing before `write()` — are ported to off-heap, where
`TestMemoryManager.limit` supplies the
same pressure and the assertions become `getUsed == 0` /
`getMemoryConsumptionForThisTask == 0`. That also moves them onto the
production allocator.
- `SpillSorterSuite` now builds one allocator per test in `beforeEach`
instead of relying on the
executor-wide singleton to hand the same instance to two call sites.
- `CometPluginsSuite`'s two override assertions become "the configured value
stands" and "nothing
is added when none was configured".
- The two `CometExecSuite` "spill sort with (multiple) dictionaries" tests
lose a `withSQLConf`
wrapper for `spark.comet.memoryOverhead` that was already inert twice
over: the suite runs
off-heap, and the config is read from `SparkConf` rather than `SQLConf`.
Local verification so far: the four regenerated diffs apply cleanly to
pristine `v3.4.3`, `v3.5.9`,
`v4.0.4` and `v4.1.3` trees; `CometDiskBlockWriterSuite` and
`SpillSorterSuite` pass. Broader suite
runs are still in progress and I will report them here. This change touches
the serde-adjacent JNI
signature, a native operator path and `dev/diffs`, so it needs the Spark SQL
and Iceberg suites
before it is queued.
--
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]