[ 
https://issues.apache.org/jira/browse/SPARK-58830?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

ASF GitHub Bot updated SPARK-58830:
-----------------------------------
    Labels: pull-request-available  (was: )

> Shuffle can silently drop data when MapStatus metadata is corrupted between 
> mapper and reducer
> ----------------------------------------------------------------------------------------------
>
>                 Key: SPARK-58830
>                 URL: https://issues.apache.org/jira/browse/SPARK-58830
>             Project: Spark
>          Issue Type: Improvement
>          Components: Spark Core
>    Affects Versions: 3.4.4
>            Reporter: wanghaihua
>            Priority: Major
>              Labels: pull-request-available
>         Attachments: shuffle-datalost.png
>
>
> h3. Problem
> {{MapStatus}} is one of the most frequently accessed metadata structures 
> throughout the shuffle lifecycle: every mapper produces one, the driver 
> retains one for each map task, and every reducer consults it to determine 
> which shuffle blocks to fetch.
> If a {{MapStatus}} instance—specifically its {{partitionLengths}} or 
> compressed representation—is corrupted in memory or in transit between the 
> mapper and reducer, a partition containing data on disk may incorrectly 
> appear to have a size of zero. Possible causes include transient hardware 
> faults, JVM or memory corruption, and serialization bugs.
> Shuffle block fetchers intentionally skip zero-length blocks when:
> {{MapStatus.getSizeForBlock(reduceId) == 0}}
> Consequently, the reducer silently skips the affected block. The valid 
> on-disk shuffle data is never read and is effectively lost, with no 
> exception, failed stage, or log signal anywhere in the pipeline.
> We observed this behavior in production. A job produced fewer output rows 
> than an otherwise equivalent rerun, despite there being no errors in the 
> driver or executor logs. Comparing the Spark UI SQL metrics from the two runs 
> revealed a significant discrepancy between shuffle “records written” and 
> “records read” at an {{Exchange}} node in the affected run.
> !shuffle-datalost.png|width=685,height=397!
> After instrumenting {{MapStatus}} at multiple points along the mapper → 
> driver → reducer path, we found that several non-empty partition entries had 
> changed to zero, while the corresponding shuffle data files on disk remained 
> complete and correct. The root cause was traced to a hardware fault that 
> silently corrupted a {{MapStatus}} object held in the driver’s memory.
> h3. Related Existing Safeguards
>  * *SPARK-35276* added checksums for shuffle data files, allowing Spark to 
> detect disk- or I/O-level corruption when a reducer reads the shuffle bytes. 
> However, when the corruption affects metadata rather than data—for example, 
> when {{MapStatus}} incorrectly reports a non-empty partition as empty—the 
> reducer never attempts to read the block, so the data-checksum path is not 
> exercised.
>  * *SPARK-40872* handles a related but distinct push-based shuffle scenario 
> in which a merged chunk is empty because it was produced by a faulty merge 
> node. It recovers by falling back to the original blocks, but it does not 
> cover non-empty-to-empty corruption in a mapper’s own {{MapStatus}} outside 
> push-based merging.
>  * *SPARK-57491* addresses a scenario where two attempts for the same 
> partition—such as speculative attempts with non-deterministic shuffle 
> keys—both complete their pushes, but only one attempt’s {{MapStatus}} 
> survives. It does not address in-memory or in-transit corruption of an 
> individual {{{}MapStatus{}}}.
> None of these safeguards detects a {{MapStatus}} that was correct when 
> produced by the mapper but became corrupted before being consumed by a 
> reducer.
> h3. Proposed Solution
> Add an integrity checksum to {{{}MapStatus{}}}, computed over the {*}set of 
> non-empty partition indices{*}, rather than over the partition sizes 
> themselves.
> Partition sizes are already represented using lossy compression or estimation 
> in {{CompressedMapStatus}} and {{{}HighlyCompressedMapStatus{}}}. Their exact 
> encoded values may therefore legitimately differ while remaining correct. For 
> this failure mode, the important invariant is whether each partition is empty 
> or non-empty.
> The checksum would travel with {{MapStatus}} throughout its normal lifecycle, 
> including Java serialization, and would be verified whenever the metadata is 
> consumed to plan shuffle block fetches.
> A checksum mismatch would indicate that the metadata was corrupted in transit 
> or at rest. Spark could then throw a {{{}FetchFailedException{}}}, reusing 
> the existing fetch-failure and stage-retry recovery path instead of silently 
> producing incomplete results.
> h3. Design Notes
>  * Store the checksum as an {{{}Option[Int]{}}}, gated by a new internal 
> configuration that is disabled by default, allowing the feature to be enabled 
> selectively.
>  * {{None}} naturally represents both “checksum verification disabled” and 
> “no checksum present,” without reserving a sentinel value in the 32-bit 
> checksum space. A computed checksum remains valid even when there are no 
> non-empty partitions.
>  * Make the checksum algorithm configurable, initially supporting {{CRC32}} 
> and {{{}Adler32{}}}.
>  * Perform verification where {{MapStatus}} is consumed to plan block fetches.
>  * On mismatch, throw {{FetchFailedException}} and reuse Spark’s existing 
> map-output fetch-failure handling rather than introducing a new recovery 
> mechanism.
>  * Verification requires {{O(N)}} time per {{{}MapStatus{}}}, where {{N}} is 
> the number of reduce partitions, plus a small constant number of bytes per 
> {{MapStatus}} for the optional checksum.
> We have a working prototype that includes:
>  * A new {{MapStatusChecksum}} utility.
>  * A {{nonEmptyChecksum: Option[Int]}} field on {{{}MapStatus{}}}.
>  * Serialization support for the checksum.
>  * Checksum verification integrated into {{{}MapOutputTracker{}}}.
> We would like to contribute this implementation upstream.



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to