-
Notifications
You must be signed in to change notification settings - Fork 370
perf: cache expected schemas for remote shuffle decoding #5722
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
pingzh
wants to merge
1
commit into
apache:main
Choose a base branch
from
pingzh:pingzh-celeborn-remote-decode
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
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
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
Oops, something went wrong.
Oops, something went wrong.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[P1] Keep the decoder alive while a read is in flight
Could creation and decoding be coordinated with this release? On supported Spark 3.4.3, a Python UDF downstream of a Celeborn exchange consumes this iterator on PythonRunner's writer thread. Its completion listener interrupts and joins that writer, but task cancellation with
interruptThread=truecan interrupt the join. TaskContextImpl catches the listener exception and continues cleanup, so Comet can reach this release while the writer is still in native decompression.fetchNext()does not take theclose()lock. This release therefore frees the boxed decoder while the active JNI call still borrows itsexpected_types, which are read after decompression indecode_remote_shuffle_batch. That introduces a use-after-free capable of crashing the executor. Previously, the types were owned locally by the JNI invocation and could not be freed by task cleanup.Please protect the handle across creation, active decoding, and release, and add coverage for interrupted cleanup overlapping a read. A latch-based test using this exact iterator and mocked JNI reproduces release while decoding is still in flight. This is source-verified reachability plus a lifecycle reproduction, not a reproduced native crash.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The P1 lifetime concern looks right to me. I reproduced the JVM half of it locally with a latch-based probe on this iterator and a mocked
Native:releaseRemoteShuffleDecoderruns to completion whiledecodeShuffleBlockWithValidationis still inside the JNI call, so nothing in the iterator serializes the release against an active decode.remoteDecoderHandleis published fromfetchNext()without the monitor thatclose()takes, anddecode_remote_shuffle_batchreadsexpected_typesafterread_ipc_compressed_validatedhas already decompressed the block, so the borrow outlives the free in that ordering. The mirror-image ordering is a leak rather than a crash, sinceclose()reads0Land the box is never freed.What I do not think is true is that this PR introduces the hazard.
CometExecIteratorhas had the same shape for a long time, with a wider window on a much hotter path. It registersclose()as a task completion listener at construction (CometExecIterator.scala:189),close()issynchronizedand callsnativeLib.releasePlan(plan)(:275and:305), andgetNextBatchcallsnativeLib.executePlan(..., plan, ...)outside that monitor (:205). On the native sidereleasePlanis aBox::from_rawdrop of the context (jni_api.rs:1051) whileexecutePlanholdsget_execution_context(exec_context), an unbounded&'a mut ExecutionContextderived from the raw handle, for the duration of an entire plan execution (jni_api.rs:848). So if the threaded Python consumer plus an interrupted cleanup wait reaches the shuffle decoder, it already reaches every Comet native plan today.Given that, I would rather not hold this PR for it. Could we file one issue covering JVM-owned native handle lifetime for both
NativeBatchDecoderIteratorandCometExecIterator, and fix them the same way? Fixing only the new handle here would leave the wider window untouched and leave us with two different ownership conventions for the same problem.For what it is worth, the minimal fix for this iterator looks like taking the monitor around just the
nativeUtil.getNextBatchcall and leavingreadNextBlock()outside it, soin.close()can still unblock a transport read. Decoding one block is bounded work, so cleanup cannot hang waiting on the monitor. Whatever we settle on should apply toreleasePlanas well.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Codex acting for the user: Fixed the
NativeBatchDecoderIteratorlifetime race in 92fa68c5b.Decoder creation, native decoding, Arrow import, and batch publication now share the monitor used by
close().next()also transfers batch ownership under that monitor. A transport read that finishes after cleanup rechecksisClosedbefore creating or using a decoder.readNextBlock()stays outside the monitor soin.close()can unblock it. Native decode failures unwind Arrow cleanup before reporting outside the monitor, since reporting may perform an RPC.Added five latch-based regressions covering creation, active decoding (including interrupted cleanup), import/publication, blocked transport reads, and failure reporting. All five failed before the fix; all 69 reader-suite tests now pass on both Spark 4.1 and Spark 3.4.3. Spotless and Scalastyle also pass.
The broader
CometExecIteratorlifetime concern remains a separate follow-up.