-
Notifications
You must be signed in to change notification settings - Fork 3.4k
Core: update manifest delete file size after rewrite table action #15470
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
amogh-jahagirdar
merged 25 commits into
apache:main
from
mbutrovich:delete_file_size_after_rewrite
Jun 27, 2026
Merged
Changes from all commits
Commits
Show all changes
25 commits
Select commit
Hold shift + click to select a range
d1234ee
Move position delete file rewriting into the manifest-writing Spark t…
mbutrovich 2852745
Fix API check?
mbutrovich 4b0bddd
Fix API check?
mbutrovich ceb65bc
Address PR feedback 2 and 4.
mbutrovich f98a692
Try to reduce revapi diff.
mbutrovich d0f9944
Try to reduce revapi diff.
mbutrovich 517c018
rebased on upstream main
mbutrovich 669e4f4
Address PR feedback.
mbutrovich 78107a1
Update revapi. Used ./gradlew :iceberg-core:revapiAcceptAllBreaks --j…
mbutrovich 57b3c43
Create Spark 3.4, 3.5, 4.0 versions.
mbutrovich 5f4ac53
Merge branch 'main' into delete_file_size_after_rewrite
mbutrovich e66e0b7
Fix after upmerge with main.
mbutrovich 2b54f86
Dedupe concurrent rewrites of the same position delete file across ma…
mbutrovich 12db8c0
Merge branch 'main' into delete_file_size_after_rewrite
mbutrovich 850c1f4
Merge remote-tracking branch 'apache/main' into delete_file_size_afte…
mbutrovich 81469d5
Address PR comments.
mbutrovich bfd6e65
Add test for position delete file in multiple snapshots.
mbutrovich 550eb80
Merge branch 'main' into delete_file_size_after_rewrite
mbutrovich 3ffa406
Fix new test setup.
mbutrovich 89b181e
Merge branch 'main' into delete_file_size_after_rewrite
mbutrovich 3ed7b95
Core, Spark: measure rewritten delete file size in a parallel phase t…
mbutrovich ecb1070
Merge branch 'main' into delete_file_size_after_rewrite
mbutrovich bbbe466
Core, Spark: dedupe DV rewrites by location and rename position-delet…
mbutrovich 1f3679f
Merge remote-tracking branch 'apache/main' into delete_file_size_afte…
mbutrovich ec2484a
Merge branch 'main' into delete_file_size_after_rewrite
mbutrovich File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -47,6 +47,7 @@ | |
| import org.apache.iceberg.puffin.PuffinReader; | ||
| import org.apache.iceberg.puffin.PuffinWriter; | ||
| import org.apache.iceberg.relocated.com.google.common.base.Preconditions; | ||
| import org.apache.iceberg.relocated.com.google.common.collect.ImmutableMap; | ||
| import org.apache.iceberg.relocated.com.google.common.collect.Lists; | ||
| import org.apache.iceberg.relocated.com.google.common.collect.Maps; | ||
| import org.apache.iceberg.relocated.com.google.common.collect.Sets; | ||
|
|
@@ -377,7 +378,12 @@ public static RewriteResult<DataFile> rewriteDataManifest( | |
| * @param stagingLocation staging location for rewritten files (referred delete file will be | ||
| * rewritten here) | ||
| * @return a copy plan of content files in the manifest that was rewritten | ||
| * @deprecated since 1.12.0, will be removed in 1.13.0; use the overload that accepts the map of | ||
| * rewritten position delete file sizes. This overload records the original {@code | ||
| * file_size_in_bytes}, which can be inconsistent with the rewritten file size on disk once | ||
| * embedded data file paths change length. | ||
| */ | ||
| @Deprecated | ||
| public static RewriteResult<DeleteFile> rewriteDeleteManifest( | ||
| ManifestFile manifestFile, | ||
| Set<Long> snapshotIds, | ||
|
|
@@ -389,6 +395,52 @@ public static RewriteResult<DeleteFile> rewriteDeleteManifest( | |
| String targetPrefix, | ||
| String stagingLocation) | ||
| throws IOException { | ||
| return rewriteDeleteManifest( | ||
| manifestFile, | ||
| snapshotIds, | ||
| outputFile, | ||
| io, | ||
| format, | ||
| specsById, | ||
| sourcePrefix, | ||
| targetPrefix, | ||
| stagingLocation, | ||
| ImmutableMap.of()); | ||
| } | ||
|
|
||
| /** | ||
| * Rewrite a delete manifest, replacing path references. | ||
| * | ||
| * <p>This is a metadata-only operation: position delete file content is rewritten separately (see | ||
| * {@link #rewritePositionDelete}). The actual sizes of those rewritten files are supplied via | ||
| * {@code rewrittenDeleteFileSizes} and recorded in the manifest so that {@code | ||
| * file_size_in_bytes} stays consistent with the rewritten file on disk. | ||
| * | ||
| * @param manifestFile source delete manifest to rewrite | ||
| * @param snapshotIds snapshot ids for filtering returned delete manifest entries | ||
| * @param outputFile output file to rewrite manifest file to | ||
| * @param io file io | ||
| * @param format format of the manifest file | ||
| * @param specsById map of partition specs by id | ||
| * @param sourcePrefix source prefix that will be replaced | ||
| * @param targetPrefix target prefix that will replace it | ||
| * @param stagingLocation staging location for rewritten position delete files | ||
| * @param rewrittenDeleteFileSizes map from source position delete file path to the actual size of | ||
| * the rewritten file; entries absent from the map keep their original size | ||
| * @return a copy plan of content files in the manifest that was rewritten | ||
| */ | ||
| public static RewriteResult<DeleteFile> rewriteDeleteManifest( | ||
| ManifestFile manifestFile, | ||
| Set<Long> snapshotIds, | ||
| OutputFile outputFile, | ||
| FileIO io, | ||
| int format, | ||
| Map<Integer, PartitionSpec> specsById, | ||
| String sourcePrefix, | ||
| String targetPrefix, | ||
| String stagingLocation, | ||
| Map<String, Long> rewrittenDeleteFileSizes) | ||
| throws IOException { | ||
| PartitionSpec spec = specsById.get(manifestFile.partitionSpecId()); | ||
| try (ManifestWriter<DeleteFile> writer = | ||
| ManifestFiles.writeDeleteManifest(format, spec, outputFile, manifestFile.snapshotId()); | ||
|
|
@@ -405,7 +457,8 @@ public static RewriteResult<DeleteFile> rewriteDeleteManifest( | |
| sourcePrefix, | ||
| targetPrefix, | ||
| stagingLocation, | ||
| writer)) | ||
| writer, | ||
| rewrittenDeleteFileSizes)) | ||
| .reduce(new RewriteResult<>(), RewriteResult::append); | ||
| } | ||
| } | ||
|
|
@@ -445,14 +498,20 @@ private static RewriteResult<DeleteFile> writeDeleteFileEntry( | |
| String sourcePrefix, | ||
| String targetPrefix, | ||
| String stagingLocation, | ||
| ManifestWriter<DeleteFile> writer) { | ||
| ManifestWriter<DeleteFile> writer, | ||
| Map<String, Long> rewrittenDeleteFileSizes) { | ||
|
|
||
| DeleteFile file = entry.file(); | ||
| RewriteResult<DeleteFile> result = new RewriteResult<>(); | ||
|
|
||
| switch (file.content()) { | ||
| case POSITION_DELETES: | ||
| DeleteFile posDeleteFile = newPositionDeleteEntry(file, spec, sourcePrefix, targetPrefix); | ||
| // Path rewrites change the file size; use the measured size, falling back to the original | ||
| // for entries that were not rewritten (e.g. deleted entries not copied to the target). | ||
| long fileSizeInBytes = | ||
| rewrittenDeleteFileSizes.getOrDefault(file.location(), file.fileSizeInBytes()); | ||
| DeleteFile posDeleteFile = | ||
| newPositionDeleteEntry(file, spec, sourcePrefix, targetPrefix, fileSizeInBytes); | ||
| appendEntryWithFile(entry, writer, posDeleteFile); | ||
| // keep the following entries in metadata but exclude them from copyPlan | ||
| // 1) deleted position delete files | ||
|
|
@@ -523,7 +582,11 @@ private static DeleteFile newEqualityDeleteEntry( | |
| } | ||
|
|
||
| private static DeleteFile newPositionDeleteEntry( | ||
| DeleteFile file, PartitionSpec spec, String sourcePrefix, String targetPrefix) { | ||
| DeleteFile file, | ||
| PartitionSpec spec, | ||
| String sourcePrefix, | ||
| String targetPrefix, | ||
| long fileSizeInBytes) { | ||
| String path = file.location(); | ||
| Preconditions.checkArgument( | ||
| path.startsWith(sourcePrefix), | ||
|
|
@@ -535,6 +598,7 @@ private static DeleteFile newPositionDeleteEntry( | |
| FileMetadata.deleteFileBuilder(spec) | ||
| .copy(file) | ||
| .withPath(newPath(path, sourcePrefix, targetPrefix)) | ||
| .withFileSizeInBytes(fileSizeInBytes) | ||
| .withMetrics(ContentFileUtil.replacePathBounds(file, sourcePrefix, targetPrefix)); | ||
|
|
||
| // Update referencedDataFile for DV files | ||
|
|
@@ -607,7 +671,11 @@ PositionDeleteWriter<Record> writer( | |
| * @param sourcePrefix source prefix that will be replaced | ||
| * @param targetPrefix target prefix to replace it | ||
| * @param posDeleteReaderWriter class to read and write position delete files | ||
| * @deprecated since 1.12.0, will be removed in 1.13.0; use {@link #rewritePositionDelete} which | ||
| * returns the size of the rewritten file so callers can record an accurate {@code | ||
| * file_size_in_bytes}. | ||
| */ | ||
| @Deprecated | ||
| public static void rewritePositionDeleteFile( | ||
| DeleteFile deleteFile, | ||
| OutputFile outputFile, | ||
|
|
@@ -617,6 +685,37 @@ public static void rewritePositionDeleteFile( | |
| String targetPrefix, | ||
| PositionDeleteReaderWriter posDeleteReaderWriter) | ||
| throws IOException { | ||
| rewritePositionDelete( | ||
| deleteFile, outputFile, io, spec, sourcePrefix, targetPrefix, posDeleteReaderWriter); | ||
| } | ||
|
|
||
| /** | ||
| * Rewrite a position delete file, replacing path references, and return the size of the rewritten | ||
| * file. | ||
| * | ||
| * <p>The size is measured from the writer after it is closed (rather than via a separate {@code | ||
| * getLength()}/HEAD call), so it is accurate even on file systems where the length of an | ||
| * in-progress write underreports. Callers record this size as {@code file_size_in_bytes} in the | ||
| * rewritten manifest. | ||
| * | ||
| * @param deleteFile source position delete file to rewrite | ||
| * @param outputFile output file to write the rewritten delete file to | ||
| * @param io file io | ||
| * @param spec spec of delete file | ||
| * @param sourcePrefix source prefix that will be replaced | ||
| * @param targetPrefix target prefix to replace it | ||
| * @param posDeleteReaderWriter class to read and write position delete files | ||
| * @return the size in bytes of the rewritten file | ||
| */ | ||
| public static long rewritePositionDelete( | ||
| DeleteFile deleteFile, | ||
| OutputFile outputFile, | ||
| FileIO io, | ||
| PartitionSpec spec, | ||
| String sourcePrefix, | ||
| String targetPrefix, | ||
| PositionDeleteReaderWriter posDeleteReaderWriter) | ||
| throws IOException { | ||
| String path = deleteFile.location(); | ||
| if (!path.startsWith(sourcePrefix)) { | ||
| throw new UnsupportedOperationException( | ||
|
|
@@ -625,8 +724,7 @@ public static void rewritePositionDeleteFile( | |
|
|
||
| // DV files (Puffin format for v3+) need special handling to rewrite internal blob metadata | ||
| if (ContentFileUtil.isDV(deleteFile)) { | ||
| rewriteDVFile(deleteFile, outputFile, io, sourcePrefix, targetPrefix); | ||
| return; | ||
| return rewriteDVFile(deleteFile, outputFile, io, sourcePrefix, targetPrefix); | ||
| } | ||
|
|
||
| // For non-DV position delete files (v2), rewrite using the reader/writer | ||
|
|
@@ -655,9 +753,14 @@ record = recordIt.next(); | |
| writer.write(newPositionDeleteRecord(record, sourcePrefix, targetPrefix)); | ||
| } | ||
| } | ||
|
|
||
| writer.close(); | ||
| return writer.length(); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| return 0; | ||
| } | ||
|
|
||
| /** | ||
|
|
@@ -668,8 +771,9 @@ record = recordIt.next(); | |
| * @param io file io | ||
| * @param sourcePrefix source prefix that will be replaced | ||
| * @param targetPrefix target prefix to replace it | ||
| * @return the size in bytes of the rewritten DV file | ||
| */ | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: add return to java docs
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. Added |
||
| private static void rewriteDVFile( | ||
| private static long rewriteDVFile( | ||
| DeleteFile deleteFile, | ||
| OutputFile outputFile, | ||
| FileIO io, | ||
|
|
@@ -708,6 +812,8 @@ private static void rewriteDVFile( | |
| try (PuffinWriter writer = | ||
| Puffin.write(outputFile).createdBy(IcebergBuild.fullVersion()).build()) { | ||
| rewrittenBlobs.forEach(writer::write); | ||
| writer.close(); | ||
| return writer.length(); | ||
| } | ||
| } | ||
|
|
||
|
|
||
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
Oops, something went wrong.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok I don't think we need to do it in this PR but after looking at this, these APIs are getting pretty unfortunate, we're passing in 10 arguments now.
I think we'll want to introduce some builders and some structure like RewriteContext, and have structures which kick back things like a DeleteManifestRewriter.
I think what I'm asking here is more fundamental changes (and possibly an overhaul/elimination of this Util class) so like I said, definitely for something in the future.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lets track this #16983