-
Notifications
You must be signed in to change notification settings - Fork 1.5k
[GH-3193] Avoid deadlock in writeAllTo by removing WritableByteChannel usage #3196
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
Conversation
|
@gszadovszky @wgtmac Would you please take a look at this patch? |
|
The new implementation is uninterruptible compared to the previous implementation that uses |
|
Thanks for the reply @gszadovszky. Although it is a bug in Spark, Spark has never triggered this bug until the Parquet version was upgraded to 1.15.1. |
|
I agree with @vrozov and @gszadovszky that Spark not Parquet should fix this. Since the root cause is not from Parquet, other user dependency may also trigger this bug and workaround on the Parquet side does not help either. |
|
@vrozov What should users do if they are still using Spark 3.0 - Spark 3.4? The Spark community no longer maintains these branches. |
|
@wgtmac possible to trigger patch releases for older Parquet branches? e.g. 1.13 (used by Spark 3.5) and 1.14 |
@wangyum Those that use Spark 3.0 - 3.4 should upgrade to Spark 3.5.x so they can continue to receive CVE and other fixes from the Spark community. Additionally from the discussion on the Apache Spark, the Parquet CVE-2025-30065 does not affect Spark, so Spark community even considered not upgrading parquet-java to 1.5.1. I also think that avoiding using As the last resort, both Spark and Parquet are open source and it is possible to recompile both using your own fork. |
Before Parquet 1.14.0. It also uninterruptible write: Lines 51 to 56 in 274dc51
|
|
It is interruptible starting with 1.14 and going back to uninterruptible is not a good option, IMO. That will even impact Spark as it will now have to write all data instead of I/O (write) being interrupted and aborted. If Spark needs this task to run without being interrupted it should use |
Rationale for this change
The reason changing the original implementation (using
Channels.newChannel(out)) to directly writing via OutputStream resolves the deadlock issue is as follows:In the original implementation, using
Channels.newChannel(out)introduces an internal lock (WritableByteChannelImpl) that interacts with the underlyingOutputStream. When Spark's task interruption mechanism (Task reaperthread) attempts to interrupt or close the channel, it acquires locks in a different order compared to the executor thread writing data. Specifically:The executor thread holds the
DFSOutputStreamlock and waits for the internal lock ofWritableByteChannelImpl.The
Task reaperthread holds the internal lock ofWritableByteChannelImpland waits for theDFSOutputStreamlock (duringhflush()).This conflicting lock acquisition order results in a deadlock.
By directly writing to the
OutputStreamwithout usingChannels.newChannel, the intermediate locking introduced byWritableByteChannelImplis eliminated. This removes the conflicting lock order scenario, thus resolving the deadlock.What changes are included in this PR?
Channels.newChannel(out)andWritableByteChannel.writeAllTomethod to write data directly to theOutputStreamusingByteBufferoperations.Are these changes tested?
Manual test.

There is no zombie task(deadlock) after applying this patch:
Are there any user-facing changes?
No.