LuciferYang opened a new pull request, #56920:
URL: https://github.com/apache/spark/pull/56920

   ### What changes were proposed in this pull request?
   
   `OneForOneBlockFetcher.DownloadCallback.onComplete` handles a chunk that has 
been
   streamed to a temporary file on disk. It calls `channel.closeAndRead()` to 
close the
   download channel and obtain a `ManagedBuffer` over the file, hands that 
buffer to the
   listener, and then either registers the temp file for later cleanup or 
deletes it:
   
   ```java
   public void onComplete(String streamId) throws IOException {
     listener.onBlockFetchSuccess(blockIds[chunkIndex], channel.closeAndRead());
     if (!downloadFileManager.registerTempFileToClean(targetFile)) {
       targetFile.delete();
     }
   }
   ```
   
   If `channel.closeAndRead()` throws an `IOException`, `onComplete` rethrew 
without any
   cleanup, so the temporary file was left on disk (it is registered for 
cleanup only
   *after* a successful read) and the channel could be left open. The sibling 
`onFailure`
   callback already performs this cleanup (`channel.close()` then 
`targetFile.delete()`);
   the success path did not.
   
   This PR guards `closeAndRead()` so that, on an `IOException`, the temp file 
is deleted
   and the channel is closed (in case it was left open) before rethrowing, 
mirroring
   `onFailure`. The buffer is handed to the listener only after 
`closeAndRead()` succeeds,
   so on the happy path ownership of the file/buffer still transfers to the 
listener
   exactly once and behavior is unchanged.
   
   ```java
   public void onComplete(String streamId) throws IOException {
     ManagedBuffer buffer;
     try {
       buffer = channel.closeAndRead();
     } catch (IOException e) {
       // closeAndRead() failed (typically from the channel close) before the 
buffer was handed
       // off, so the temp file was never registered for cleanup. Delete it, 
and close the
       // channel in case it was left open, mirroring onFailure, instead of 
leaking them.
       channel.close();
       targetFile.delete();
       throw e;
     }
     listener.onBlockFetchSuccess(blockIds[chunkIndex], buffer);
     if (!downloadFileManager.registerTempFileToClean(targetFile)) {
       targetFile.delete();
     }
   }
   ```
   
   Both in-tree `DownloadFileWritableChannel` implementations
   (`SimpleDownloadFile.SimpleDownloadWritableChannel` and
   `BlockManager.EncryptedDownloadWritableChannel`) implement `closeAndRead()` 
as
   "close the channel, then construct the buffer", so the most realistic source 
of an
   `IOException` is the channel close itself; the added `channel.close()` is 
idempotent
   and harmless when the channel is already closed.
   
   ### Why are the changes needed?
   
   When fetching shuffle blocks to disk, an `IOException` from `closeAndRead()` 
orphaned
   the temporary block file (and potentially leaked an open channel/file 
descriptor). The
   external shuffle service and executors are long-lived, so these orphaned 
temp files
   accumulate under the local directories until the process exits. This aligns 
the success
   path with the already-correct cleanup in `onFailure`.
   
   ### Does this PR introduce _any_ user-facing change?
   
   No.
   
   ### How was this patch tested?
   
   Added a unit test 
`testFetchToDiskClosesAndDeletesTempFileWhenCloseAndReadFails` in
   `OneForOneBlockFetcherSuite` that drives the fetch-to-disk path with a
   `DownloadFileWritableChannel` whose `closeAndRead()` throws, and verifies 
that the
   channel is closed, the temp file is deleted, the file is never registered 
for later
   cleanup, and the buffer is never handed to the listener. The test fails 
without the
   fix (the channel is never closed) and passes with it.
   
   `build/sbt 'network-shuffle/testOnly *OneForOneBlockFetcherSuite'` -> 14 
tests pass.
   
   ### Was this patch authored or co-authored using generative AI tooling?
   
   Generated-by: Claude Code
   


-- 
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]

Reply via email to