[1.11.x] Core, Spark: Do not rewrite delete files excluded from the copy plan - #17514
Open
vgkowski wants to merge 1 commit into
Open
[1.11.x] Core, Spark: Do not rewrite delete files excluded from the copy plan#17514vgkowski wants to merge 1 commit into
vgkowski wants to merge 1 commit into
Conversation
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.
Closes #17498.
This targets
1.11.xonly.mainis not affected: there,positionDeletesToRewrite()collects the files to rewrite from live manifest entries, so the bug does not exist and there is no correspondingmainPR to backport from.Problem
RewriteTablePathUtil.writeDeleteFileEntryapplies a filter when building the copy plan: only entries that are live and added within the requested delta are copied. Entries that areDELETED(history markers) or outside the delta are still written into the rewritten manifest, but excluded from the copy plan.The physical-rewrite set did not apply the same filter — every position delete entry was queued unconditionally:
Position delete files must be physically rewritten because they embed absolute data file paths, so
RewriteTablePathSparkActionopens every file intoRewrite. When the entry is aDELETEDmarker whose underlying file was already removed byexpire_snapshots, the open fails and the whole procedure aborts withNotFoundException/NoSuchKey— even though that file was never going to be copied.Because the
DELETEDentry is baked into the current snapshot's delete manifest, the failure is not transient: every subsequentrewrite_table_pathrun fails the same way, breaking both full rewrites of such a table and incremental rewrites whose delta spans the expiry.Fix
Align the physical-rewrite set with the copy plan: an entry that is not copied is kept in the rewritten manifest exactly as the source has it, and is no longer opened. This makes the position-delete branch consistent with the data-file and equality-delete branches, which already behave this way.
This is safe because
DELETEDentries are never opened by readers (they exist only so the manifest history stays consistent), and out-of-deltaEXISTINGentries point at files the target already has from a previous incremental run. A live, still-needed delete file cannot be skipped as a result of expiry, sinceexpire_snapshotsnever deletes a file that remains reachable from a retained snapshot.Side effect worth noting:
rewrittenDeleteFilePathsCountin the action result now only counts files that are actually copied. It previously also counted dead entries and entries outside the delta, which were staged and then discarded.Tests
Two regression tests, added to all Spark versions (3.4, 3.5, 4.0, 4.1), which share the fixed core code path but run their CI independently:
rewriteAfterExpiringDeletedPositionDeleteFile— a deletion vector is dropped by compaction, leaving aDELETEDentry in the current snapshot's delete manifest, andexpire_snapshotsthen removes the file from storage. A full rewrite must not attempt to physically rewrite that entry. Without the fix this fails withNotFoundExceptionfromPuffinReader.incrementalRewriteAfterExpiringDeletedPositionDeleteFile— covers the second failure mode: an incremental rewrite whosestart..enddelta spans compaction and expiry of a position delete file must keep the surviving entry out of the physical-rewrite set.Both assert
rewrittenDeleteFilePathsCount()and compare target rows against source rows. They are limited to format version 3 and later: in v2, compaction does not remove the position delete file from storage, so the scenario does not arise.