Core: Read DELETE manifests in ReachableFileCleanup - #17745
Open
dkranchii wants to merge 1 commit into
Open
Conversation
dramaticlly
reviewed
Aug 21, 2026
ManifestFiles.readPaths previously required a DATA manifest and threw IllegalArgumentException on DELETE manifests. ReachableFileCleanup calls readPaths on every manifest reachable only through expired snapshots; when one was a DELETE manifest (as happens after a branch that added delete files is removed, or any expire with withIncrementalCleanup(false)), the exception was logged and swallowed by the suppressFailureWhenFinished task loop, silently orphaning the referenced delete and DV files on object storage. Fix readPaths to dispatch through ManifestFiles.open so it works for both DATA and DELETE manifests. This centralizes the fix (readPaths has no other callers) and keeps ReachableFileCleanup unchanged. Add a regression test in TestRemoveSnapshots that exercises the branch removal path where a delete manifest is uniquely owned by an expired snapshot.
dkranchii
force-pushed
the
core-reachable-cleanup-delete-manifests
branch
from
August 21, 2026 16:18
c20825a to
557a788
Compare
Contributor
|
thanks @dkranchii for the contribution, but I think the new test To be clear, the delete manifests seem to be GCed correctly during snapshot expiration as verified in the existing test iceberg/core/src/test/java/org/apache/iceberg/TestRemoveSnapshots.java Lines 1040 to 1094 in 7f879b1 Also, I believe it would be great to expand the TestRemoveSnapshots coverage for v3 table as well, if you are willing to help. |
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.
Summary
ReachableFileCleanup#findFilesToDeleteusesManifestFiles.readPathsto enumerate live file paths for every manifest scheduled for deletion.readPathsis only defined for DATA manifests — it callsManifestFiles.readwhich assertsmanifest.content() == ManifestContent.DATAand throwsIllegalArgumentExceptionotherwise. Because the surroundingTasks.foreach(...).retry(3).suppressFailureWhenFinished()catches onlyIOExceptionexplicitly and suppresses everything else, the exception is logged per retry and swallowed, so DELETE manifests reachable only through expired snapshots are silently skipped. The delete files and DV Puffin blobs they reference are left as orphans on object storage.ReachableFileCleanupis selected byRemoveSnapshots.cleanExpirenapshotswhenever specific snapshot IDs are given, when a non-main snapshot was removed, or when there are non-main snapshots — i.e., any workflow that touches branches or tags.IncrementalFileCleanup.findFilesToDeletealready handles this correctly by usingManifestFiles.open, which returns aManifestReaderfor either DATA or DELETE manifest content. This PR alignsReachableFileCleanupwith that pattern and adds a regression test.Change
ManifestFiles.readPaths(...)calls inReachableFileCleanup#findFilesToDeletewithManifestFiles.open(...).select("file_path").liveEntries(), projecting only the file-path column so the read cost is unchanged.TestRemoveSnapshots#testReachableCleanupWithDeleteManifestFromRemovedBranchstages a position-delete file on a branch, removes the branch, expires older snapshots, and asserts the delete-file location is enumerated for deletion. Gated withassumeThat(formatVersion == 2)andassumeThat(!incrementalCleanup)to target t reachable cleanup path.Test plan
./gradlew :iceberg-core:test --tests "org.apache.iceberg.TestRemoveSnapshots"passes../gradlew spotlessCheckpasses.AI Disclosure