Repository navigation
fix(spark): release native read resources deterministically - #103
Merged
Merged
Conversation
Close data sources and scans explicitly across reader and footer lifecycles. Release prefetched partitions and make Arrow stream handoff failures exception-safe, with native-ownership regression coverage. Fixes #96 Signed-off-by: Christopher Wirt <[email protected]>
- Flatten Partition.scanArrow failure handling. - Drop the unused currentPartition field from VortexPartitionReader. - Test that a closed Arrow reader leaves its allocator empty. Co-Authored-By: Claude Opus 5.5 <[email protected]> Signed-off-by: Christopher Wirt <[email protected]>
Signed-off-by: Christopher Wirt <[email protected]> (cherry picked from commit bfbc5bb) Signed-off-by: Christopher Wirt <[email protected]>
ChristopherWirt
force-pushed
the
cwirt/read-native-resource-cleanup
branch
from
November 9, 2026 16:53
0d24b2e to
15dad52
Compare
a392n328
approved these changes
Nov 9, 2026
This was referenced Nov 9, 2026
ChristopherWirt
added a commit
that referenced
this pull request
Nov 10, 2026
## Summary Fix the shared Linux musl builder failure on `develop` and PRs #103, #105, #106, #107, and #108 after the upstream lazy-constructor update. ## Changes Normalize lists rebuilt from list views through the same `reset_offsets` path as ordinary lists before forwarding their children to layout writers. Rebuilding a sliced list view can now leave a lazy elements slice; a flat child writer cannot serialize that expression. This preserves the existing referenced-element bounds and resolves the slice without forcing unrelated encodings into canonical arrays. Keep the existing nullable sliced-list-view regression and add the non-nullable case. ## CI Failure Analysis ### Status Pre-existing base-branch regression, independent of the individual PR changes. The same failure appears on [develop run 42649358621](https://github.com/quantcast/vortex/actions/runs/42649358621) and every completed failing PR builder inspected (#105–#108). ### Failed Jobs - `Rust tests (linux-musl)`: test failure in `layouts::list::reader::tests::sliced_list_chunks_round_trip::case_4_list_view_chunks`. ### Relevant Log Output ```text Error: Array vortex.slice is not registered for serialization ``` ### Correlation With PR Changes The upstream merge makes array slicing lazy. The list-view branch of `canonicalize_to_list_parts` forwarded a rebuilt list's lazy elements directly, while the ordinary-list branch already normalized them. The Spark PRs do not change this writer; #107 changes separate zone-pruning code. ### Recommended Next Step Merge this shared fix into `develop`. The same patch is being applied to the affected open PR branches so their new builders include the fix immediately. ## Validation - Reproduced the exact failing test on unmodified current `develop` (exit 100, same serialization error). - `cargo nextest run -p vortex-layout -E 'test(layouts::list::)'`: all 161 tests passed after the fix, including the original failing case and new non-nullable case. - `cargo clippy -p vortex-layout --all-targets --all-features -- -D warnings`: passed. - Pinned nightly Rust formatting and `git diff --check`: passed. Local validation ran on macOS; the fresh Linux musl builders will verify the runner-specific job. Signed-off-by: Christopher Wirt <[email protected]>
robert3005
pushed a commit
to vortex-data/vortex
that referenced
this pull request
Nov 11, 2026
## Summary Fixes #11364. Closing a Spark reader currently leaves its Java-owned native read handles and file caches alive until Cleaner processing. Make that release explicit and idempotent, while preserving the resources still needed by derived scans, returned partitions and Arrow batches. Ports the lifecycle fix from [QC #103](quantcast#103). ## Changes - Make `DataSource` and `Scan` implement `AutoCloseable`, retaining Cleaner fallback. Keep native calls synchronized with explicit close; fence scan options so their expressions remain alive across JNI. - Release partitions prefetched by `hasNext()` but never returned. - Let Spark's owning wrappers close at EOF. Reader and footer cleanup attempts every close in ownership order and preserves the original failure. - Release native partition ownership when Arrow stream allocation or import fails. - Reject reads on closed handles with `IllegalStateException`. An open, exhausted scan still returns `false` from `hasNext()` and throws `NoSuchElementException` from `next()`. --------- Signed-off-by: Christopher Wirt <[email protected]>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
A Spark Vortex reader that finishes or closes leaves its native
DataSource(and that source's file cache) alive until Java GC runs the Cleaner. Completed readers pile up native memory even when no Arrow batch is open.Fixes #96.
Fix
DataSourceandScanare nowAutoCloseable.close()is idempotent. After it, calls throwIllegalStateException, exceptScan.hasNext(), which returnsfalse. Cleaner remains the fallback. Scans, partitions and batches already handed out stay valid.Scan.close()also frees a partition thathasNext()prefetched butnext()never returned.VortexPartitionReadercloses everything at EOF, on early close, and on failure: batch → Arrow reader → scan → source → readable. It attempts every close and keeps the original exception. Empty splits and footer reads follow the same rules.Partition.scanArrowis exception-safe. If stream allocation or Arrow import fails (e.g. a bounded allocator), the native partition or exported stream is released.API changes
DataSourceandScanimplementAutoCloseable, and their methods aresynchronizedsoclose()can't race a native call.Results (local, dev native lib)
Rows, checksums, allocation counts and warmed read times are unchanged. These are local measurements, not production OOM or throughput claims.
Testing
ReadLifecycleTest(JNI) andVortexPartitionReaderLifecycleTest(Spark) cover: idempotent close, release without GC, primed-scan release, bounded-allocator failures, and an empty allocator after a reader closes.ReadLifecycleTest/TestMinimal/NativeIOBridgeTest, plus reader-lifecycle and footer tests on Spark 4.0 and Spark 3.5 (Scala 2.12 and 2.13), plus Spark 4.0 count tests.Upstream
Upstream
developstill frees source/scan handles only through the Cleaner. No equivalent upstream issue or PR exists. vortex-data#8075 (session lifetime) and vortex-data#11123/vortex-data#11124/vortex-data#11127/vortex-data#11197/vortex-data#11198 (decode/output buffering) are related but separate.🤖 Generated with Claude Code