[SPARK-58107][CORE][FOLLOWUP] Keep the local-checkpoint sealed checksum as a tombstone past last-replica loss#57499
Closed
juliuszsompolski wants to merge 1 commit into
Conversation
…um as a tombstone past last-replica loss A sealed local-checkpoint block's checksum was dropped from the master when the block's last replica went away (empty-locations branches of updateBlockInfo / removeBlockManager). After a checkpoint is sealed and its lineage cut, losing the winning replica then erased the seal, so a late divergent copy of that block (e.g. a zombie task from a superseded stage attempt) had nothing to reject it in checksumSealRejectsUpdate and later readers could silently observe a different version. Keep sealedChecksums as a tombstone: it survives the loss of the last replica (a matching late copy is admitted, a divergent one rejected) and is reclaimed only with the RDD in removeRdd, via a new sealedBlocksByRdd index so the reclaim is O(the RDD's sealed blocks) rather than a full-map scan. Only sealedChecksums is tombstoned; blockChecksums is still dropped eagerly.
Contributor
Author
|
cc @cloud-fan |
cloud-fan
approved these changes
Jul 24, 2026
cloud-fan
left a comment
Contributor
There was a problem hiding this comment.
0 blocking, 0 non-blocking, 0 nits.
The metadata lifetime, rejection path, and RDD-scoped cleanup are internally consistent, with focused regression coverage for the changed behavior.
Verification
Traced sealRddChecksums through both replica-removal paths and removeRdd, confirmed the reverse index is populated only alongside a sealed checksum and drained at RDD removal, and independently checked the new tests against those production paths. The text and contract scanners examined all prepared candidates and reported no findings.
cloud-fan
pushed a commit
that referenced
this pull request
Jul 24, 2026
…um as a tombstone past last-replica loss ### What changes were proposed in this pull request? Follow-up to [SPARK-58107](https://issues.apache.org/jira/browse/SPARK-58107) (detect and repair non-deterministic local checkpoints via RDD-block content checksums). Makes the master's per-block sealed checksum (`sealedChecksums` in `BlockManagerMasterEndpoint`) a tombstone that survives the loss of the block's last replica and is reclaimed only when the RDD is removed. Previously `sealedChecksums(blockId)` was dropped alongside `blockLocations` when a block's last replica went away (the empty-locations branches of `updateBlockInfo` and `removeBlockManager`). - `updateBlockInfo` / `removeBlockManager`: on empty locations, keep `sealedChecksums` (still drop `blockLocations` / `blockChecksums`). A late replica matching the seal is admitted, a divergent one rejected by `checksumSealRejectsUpdate`. - `removeRdd`: reclaim the RDD's tombstones via a new `sealedBlocksByRdd` index (rddId -> its sealed block ids, populated in `sealRddChecksums`), so reclaim is O(the RDD's sealed blocks) rather than a scan of the whole `sealedChecksums` map on the single-threaded endpoint. - Only `sealedChecksums` (a `Long` per sealed partition, plus its `RDDBlockId` in the index) is tombstoned; `blockChecksums` is still dropped at empty-locations. ### Why are the changes needed? After a checkpoint is sealed and its lineage is cut, losing the winning replica erased the seal. A late divergent copy of that block -- e.g. from a zombie task of a superseded stage attempt that was still running from before the seal -- then had no seal to reject it, so `updateBlockInfo` admitted it and later readers could silently observe a different version than earlier readers. Losing every replica of a finalized checkpoint must mean data loss, not a silent version switch. The tombstone preserves the seal so the divergent late copy is still rejected; retention is bounded by RDD lifetime (reclaimed in `removeRdd`). ### Does this PR introduce _any_ user-facing change? No. `spark.checkpoint.local.verifyChecksum.enabled` gates the seal path; behavior only differs when it is enabled, and only in the (previously silently wrong) last-replica-loss + divergent-late-copy case. ### How was this patch tested? New `BlockManagerSuite` tests drive the master RPC sequence the race produces (no scheduler needed): after a sealed block loses its last replica -- via an invalid-level `updateBlockInfo` and via `removeExecutor` -- the seal survives as a tombstone, a divergent late registration is rejected and a matching one admitted; and `removeRdd` reclaims a zero-replica tombstone. The existing "sealed checksums are cleared when the RDD is removed" test still passes. ### Was this patch authored or co-authored using generative AI tooling? Generated-by: Claude Code (Opus 4.8) Closes #57499 from juliuszsompolski/julek_data/checksum-seal-verify-tombstone. Authored-by: Juliusz Sompolski <julek@databricks.com> Signed-off-by: Wenchen Fan <wenchen@databricks.com> (cherry picked from commit b65ef3e) Signed-off-by: Wenchen Fan <wenchen@databricks.com>
Contributor
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?
Follow-up to SPARK-58107 (detect and repair
non-deterministic local checkpoints via RDD-block content checksums). Makes the master's per-block
sealed checksum (
sealedChecksumsinBlockManagerMasterEndpoint) a tombstone that survives theloss of the block's last replica and is reclaimed only when the RDD is removed.
Previously
sealedChecksums(blockId)was dropped alongsideblockLocationswhen a block's lastreplica went away (the empty-locations branches of
updateBlockInfoandremoveBlockManager).updateBlockInfo/removeBlockManager: on empty locations, keepsealedChecksums(still dropblockLocations/blockChecksums). A late replica matching the seal is admitted, a divergent onerejected by
checksumSealRejectsUpdate.removeRdd: reclaim the RDD's tombstones via a newsealedBlocksByRddindex (rddId -> its sealedblock ids, populated in
sealRddChecksums), so reclaim is O(the RDD's sealed blocks) rather than ascan of the whole
sealedChecksumsmap on the single-threaded endpoint.sealedChecksums(aLongper sealed partition, plus itsRDDBlockIdin the index) istombstoned;
blockChecksumsis still dropped at empty-locations.Why are the changes needed?
After a checkpoint is sealed and its lineage is cut, losing the winning replica erased the seal. A
late divergent copy of that block -- e.g. from a zombie task of a superseded stage attempt that was
still running from before the seal -- then had no seal to reject it, so
updateBlockInfoadmitted itand later readers could silently observe a different version than earlier readers. Losing every
replica of a finalized checkpoint must mean data loss, not a silent version switch. The tombstone
preserves the seal so the divergent late copy is still rejected; retention is bounded by RDD lifetime
(reclaimed in
removeRdd).Does this PR introduce any user-facing change?
No.
spark.checkpoint.local.verifyChecksum.enabledgates the seal path; behavior only differs when itis enabled, and only in the (previously silently wrong) last-replica-loss + divergent-late-copy case.
How was this patch tested?
New
BlockManagerSuitetests drive the master RPC sequence the race produces (no scheduler needed):after a sealed block loses its last replica -- via an invalid-level
updateBlockInfoand viaremoveExecutor-- the seal survives as a tombstone, a divergent late registration is rejected and amatching one admitted; and
removeRddreclaims a zero-replica tombstone. The existing "sealedchecksums are cleared when the RDD is removed" test still passes.
Was this patch authored or co-authored using generative AI tooling?
Generated-by: Claude Code (Opus 4.8)