Apache Iceberg version
main (development)
Please describe the bug 🐞
Manifests with only DELETEs are not pruned when creating new snapshots, causing large numbers of extra manifests to accumulate.
overwriteFiles.existingManifests iterates each parent snapshot manifest with discardDeleted=true, which skips entries with status=DELETED. For a manifest where every entry is DELETED, the iterator yields nothing (since foundDeletedCount=0 and len(notDeleted)=0) which the code interprets as "manifest was unaffected by this operation" and retains instead of dropping it at
|
if foundDeletedCount == 0 { |
|
existingFiles = append(existingFiles, m) |
|
|
|
continue |
|
} |
I think the fix is small, at L190, only retain the manifest if the iterator actually found live entries:
if foundDeletedCount == 0 {
- existingFiles = append(existingFiles, m)
+ if len(notDeleted) > 0 {
+ existingFiles = append(existingFiles, m)
+ }
+ // len(notDeleted)==0: all entries are DELETED and were skipped by
+ // discardDeleted=true. The manifest is empty — drop it.
continue
}
We observed this when using overwrite on small tables, which produces one DELETE manifest via deletedFilesManifests. Since these are never pruned, they accumulate: after N overwrites the current snapshot's manifest list contains N all-DELETED manifests alongside the live data manifests, causing a growing set of unnecessary snapshots.
Apache Iceberg version
main (development)
Please describe the bug 🐞
Manifests with only DELETEs are not pruned when creating new snapshots, causing large numbers of extra manifests to accumulate.
overwriteFiles.existingManifestsiterates each parent snapshot manifest withdiscardDeleted=true, which skips entries withstatus=DELETED. For a manifest where every entry isDELETED, the iterator yields nothing (sincefoundDeletedCount=0andlen(notDeleted)=0) which the code interprets as "manifest was unaffected by this operation" and retains instead of dropping it aticeberg-go/table/snapshot_producers.go
Lines 190 to 194 in a7c4912
I think the fix is small, at L190, only retain the manifest if the iterator actually found live entries:
if foundDeletedCount == 0 { - existingFiles = append(existingFiles, m) + if len(notDeleted) > 0 { + existingFiles = append(existingFiles, m) + } + // len(notDeleted)==0: all entries are DELETED and were skipped by + // discardDeleted=true. The manifest is empty — drop it. continue }We observed this when using overwrite on small tables, which produces one DELETE manifest via
deletedFilesManifests. Since these are never pruned, they accumulate: after N overwrites the current snapshot's manifest list contains N all-DELETED manifests alongside the live data manifests, causing a growing set of unnecessary snapshots.