[SPARK-36284][CORE][SHUFFLE] Add shuffle checksum support for push-based shuffle - #57748
Open
Dreamstick9 wants to merge 2 commits into
Open
[SPARK-36284][CORE][SHUFFLE] Add shuffle checksum support for push-based shuffle#57748Dreamstick9 wants to merge 2 commits into
Dreamstick9 wants to merge 2 commits into
Conversation
…sed shuffle The external shuffle service now calculates a checksum per chunk of a merged shuffle partition while it merges the pushed blocks, and stores them next to the merged data, index and meta files. A new DiagnoseShuffleChunkCorruption request asks the shuffle service which merged a chunk for the cause of its corruption, so a corrupted merged shuffle chunk is diagnosed the same way a corrupted shuffle block has been since SPARK-36206, instead of falling back to the original shuffle blocks without reporting anything. The running checksum of a chunk is fed as the blocks are written, and is recalculated from the merged data file when it stops describing the chunk, which happens when a partially written block is abandoned and overwritten, or when a block is still being written as the shuffle merge is finalized. The checksums are strictly diagnostic: a failure to keep them up to date gives up on the checksums of that partition rather than failing the merge.
uros-b
reviewed
Aug 4, 2026
| <td> | ||
| Whether the external shuffle service calculates the checksum of every chunk of a merged shuffle partition while it merges the pushed blocks. The checksums are stored next to the merged shuffle data and are only used to diagnose the cause of a corrupted shuffle chunk, in the same way <code>spark.shuffle.checksum.enabled</code> is used for the shuffle blocks that are not merged. Diagnosing a corrupted shuffle chunk additionally requires <code>spark.shuffle.checksum.enabled</code> to be set in the application reading the chunk. | ||
| </td> | ||
| <td>4.3.0</td> |
Member
There was a problem hiding this comment.
Just a drive-by note: branch-4.3 has already been cut, so double-check whether this is intended to be backported there as well
Author
There was a problem hiding this comment.
hmm nice catch.the version numbers in this PR were out of date. dev/next_version_candidates.py now shows 4.4.0 for branch-4.x, and branch-4.3 is cut. I changed the two configuration rows and the @since tag to 4.4.0.This change is a new feature. Therefore, it goes to master and branch-4.x only. I do not backport it to branch-4.3.
…nfigurations branch-4.3 has been cut, so dev/next_version_candidates.py now reports 4.4.0 for branch-4.x. This is a new feature, so it is not backported to the already cut branch-4.3 and first ships in 4.4.0.
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.
What changes were proposed in this pull request?
SPARK-36206 added shuffle corruption diagnosis based on shuffle checksums, but only for shuffle
blocks that are not merged. When a push-merged shuffle chunk is detected as corrupt, the reducer
falls back to the original shuffle blocks and reports nothing about the cause, because no checksum
of the merged data exists. SPARK-37695 made that explicit by skipping the diagnosis of a
ShuffleBlockChunkIdwith aTODO SPARK-36284. This PR fills that gap.Server side,
RemoteBlockPushResolvernow calculates a checksum per chunk of a merged shufflepartition while it merges the pushed blocks, and stores them next to the merged data, index and
meta files as
shuffleMerged_<appId>_<shuffleId>_<shuffleMergeId>_<reduceId>.checksum.<ALGORITHM>(one long per chunk, so one entry less than the index file, which has a leading zero offset). The
checksum algorithm is part of the file name, as it is for the checksum file of a non-merged shuffle
block, so the checksums can never be compared against those of a different algorithm after the
shuffle service is reconfigured.
The running checksum of the current chunk is fed in
writeBuf, right after the data of a block hasbeen written, so merging does not read the merged data back in the common case. Two situations make
the running checksum stop describing the current chunk, and both are detected by tracking the
position up to which the checksum has consumed the data file:
since
dataFilePosonly advances when a block commits;away.
In both cases the checksum of the chunk is recalculated from the merged data file when the chunk
gets sealed. The checksum entry is written from
updateChunkInfo, together with the chunk offsetand the chunk bitmap, and it follows the same tracked-position and rewind-on-retry protocol, so the
checksum file stays aligned with the index file even when the update of the index or meta file
fails and is retried.
Client side, a new
DiagnoseShuffleChunkCorruptionmessage asks the shuffle service which merged achunk for the cause of its corruption, and the response reuses
CorruptionCause.ShuffleBlockFetcherIteratornow diagnoses a corrupt shuffle chunk before it falls back to theoriginal shuffle blocks. The diagnosis of a merged chunk is answered by comparing the checksum the
reducer calculated against the stored one and against a recalculation of the chunk on disk, exactly
like
ExternalShuffleBlockResolver.diagnoseShuffleBlockCorruptiondoes for a non-merged block, soDISK_ISSUE,NETWORK_ISSUEandCHECKSUM_VERIFY_PASSkeep their existing meanings. A chunk thatwas merged with a different algorithm than the reducer uses is reported as
UNSUPPORTED_CHECKSUM_ALGORITHM.Two shuffle service configurations are added:
spark.shuffle.push.server.mergedShuffleChecksum.enabledtruespark.shuffle.push.server.mergedShuffleChecksum.algorithmADLER32The default algorithm matches the default of
spark.shuffle.checksum.algorithm, so the two line upout of the box. An unsupported algorithm only disables the calculation with a warning instead of
failing the merge.
The checksums are strictly diagnostic, so nothing about them is allowed to affect the merge. A
failure to write or recompute them gives up on the checksums of that shuffle partition, logs a
warning and deletes the checksum file when the partition is finalized, instead of counting towards
spark.shuffle.push.server.ioExceptionsThresholdDuringMergeor failing the finalization of anotherwise correctly merged partition. A chunk of such a partition then simply diagnoses as
UNKNOWN_ISSUE.Notes for reviewers, mostly on the trade-offs I would like your opinion on:
corruption of the merged data on the shuffle service disk and in transit to the reducer. It
cannot distinguish corruption that already happened while a mapper pushed the block, which is
noted in the code.
original shuffle blocks exactly as before, since a chunk is never re-fetched. It does however
delay that fallback by the drain of the chunk and one synchronous RPC. Like the existing
diagnosis of a shuffle block, that RPC uses
spark.network.timeoutfor both the connection andthe request, so a shuffle service that stops responding between the fetch of a chunk and its
diagnosis can delay by up to twice that timeout a fallback which is immediate today. The
connection is normally still cached from the fetch of the chunk itself, so this only shows up
when the shuffle service dies in that window, but it is the one behaviour change here that
makes a recoverable path slower. Happy to gate it on a separate configuration, or to skip the
diagnosis for chunks whose shuffle service just failed, if you prefer either.
spark.shuffle.push.server.mergedShuffleChecksum.enabledistrue, matchingspark.shuffle.checksum.enabled. It costs one checksum pass over the merged data, one extrafile, and one more file descriptor per actively merging partition, which is held for as long as
the index and meta file descriptors are. The checksum file is written exactly as often as the
index file, once per sealed chunk, so it follows the same
MergeShuffleFilepattern, but itcould be opened only when a chunk is sealed if the fourth descriptor is a concern. Please say
so if you would rather have the whole thing off by default.
UNSUPPORTED_CHECKSUM_ALGORITHMeven though both algorithms are supported on their own, sinceappending a new
Causewould break a reducer running an older Spark version. The shuffleservice logs which two algorithms disagree.
MergedShuffleFileManagergains a default method that returnsUNKNOWN_ISSUE, so third partyimplementations and
NoOpMergedShuffleFileManagerkeep working unchanged.Why are the changes needed?
Push-based shuffle is the only place where Spark detects shuffle data corruption and cannot say
anything about its cause. When a merged chunk is corrupt, the reducer silently falls back to the
original blocks, so a shuffle service with a failing disk keeps corrupting merged data and the only
symptom is degraded performance from repeated fallbacks. With the merged chunks checksummed, the
same diagnosis that is available for regular shuffle blocks since Spark 3.2 tells the user whether
the corruption came from the shuffle service disk or from the network.
Does this PR introduce any user-facing change?
Yes. Two new shuffle service configurations, documented in
docs/configuration.md. With thedefault configuration a shuffle service with push-based shuffle enabled writes one additional
checksum file per merged shuffle partition, and the reducer logs the diagnosed cause of a corrupt
merged shuffle chunk, for example:
Previously the corruption of a merged chunk was reported as:
How was this patch tested?
New unit tests, plus existing ones extended:
RemoteBlockPushResolverSuite: the sharedvalidateChunkshelper now also verifies that everystored checksum matches the data its chunk ended up with in the merged shuffle data file, so all
the existing merge tests cover the checksums, including the ones that inject
IOExceptions intothe index and meta file updates. New tests cover the checksums of the merged chunks and the entry
count against the index file, a chunk whose data is overwritten after a block push failed, a
chunk sealed while a block push is still in flight at finalization, the four diagnosis outcomes
(
CHECKSUM_VERIFY_PASS,NETWORK_ISSUE,DISK_ISSUEagainst a data file corrupted after themerge, and
UNSUPPORTED_CHECKSUM_ALGORITHM), diagnosis of an unknown chunk, diagnosis when thechecksum calculation is disabled, and a checksum file that cannot be written, which must leave
the merge and its
MergeStatusesuntouched. Cleanup assertions for the checksum file were addedto the outdated shuffle merge id and
removeShuffleMergetests.ExternalBlockHandlerSuite: the new RPC is routed to theMergedShuffleFileManagerand answeredwith a
CorruptionCause.BlockTransferMessagesSuite: round trip ofDiagnoseShuffleChunkCorruption, and ofDiagnoseCorruptionandCorruptionCause, which were not covered before.ShuffleBlockFetcherIteratorSuite: the diagnosis of aShuffleBlockChunkIdreaches the shuffleservice which merged the chunk, using the external shuffle service port for a push-merged-local
chunk, and a corrupt merged chunk is diagnosed before it falls back to the original blocks.
Was this patch authored or co-authored using generative AI tooling?
Generated-by: Claude Code 2.1.220