tanmayrauth commented on code in PR #1392:
URL: https://github.com/apache/iceberg-go/pull/1392#discussion_r3525696458


##########
table/rewrite_data_files.go:
##########
@@ -300,38 +300,61 @@ func ExecuteCompactionGroup(ctx context.Context, tbl 
*Table, group CompactionTas
                scanOpts = append(scanOpts, 
WitMaxConcurrency(cfg.scanConcurrency))
        }
 
+       if tbl.metadata.Version() >= 3 {
+               lineageTasks, legacyTasks := splitTasksByLineage(group.Tasks)
+               if len(lineageTasks) > 0 && len(legacyTasks) > 0 {
+                       slog.Info("compaction group has mixed row-lineage 
tasks; splitting to preserve _row_id for lineage-capable files",
+                               "partition_key", group.PartitionKey,
+                               "lineage_tasks", len(lineageTasks),
+                               "legacy_tasks", len(legacyTasks))
+
+                       lineageGroup := CompactionTaskGroup{
+                               PartitionKey:   group.PartitionKey,
+                               Tasks:          lineageTasks,
+                               TotalSizeBytes: sumTaskBytes(lineageTasks),
+                       }
+                       legacyGroup := CompactionTaskGroup{
+                               PartitionKey:   group.PartitionKey,
+                               Tasks:          legacyTasks,
+                               TotalSizeBytes: sumTaskBytes(legacyTasks),
+                       }
+
+                       preserve, err := executeCompactionTaskGroup(ctx, tbl, 
scanOpts, lineageGroup, cfg.targetFileSize, true)
+                       if err != nil {
+                               return CompactionGroupResult{}, err
+                       }
+                       legacy, err := executeCompactionTaskGroup(ctx, tbl, 
scanOpts, legacyGroup, cfg.targetFileSize, false)
+                       if err != nil {
+                               return CompactionGroupResult{}, err
+                       }
+
+                       safePosDeletes := 
mergeTaskFiles(preserve.SafePosDeletes, legacy.SafePosDeletes)
+                       safeDeletionVectors := 
mergeTaskFiles(preserve.SafeDeletionVectors, legacy.SafeDeletionVectors)
+
+                       return CompactionGroupResult{
+                               PartitionKey:        group.PartitionKey,
+                               OldDataFiles:        
append(preserve.OldDataFiles, legacy.OldDataFiles...),
+                               NewDataFiles:        
append(preserve.NewDataFiles, legacy.NewDataFiles...),
+                               SafePosDeletes:      safePosDeletes,
+                               SafeDeletionVectors: safeDeletionVectors,
+                               BytesBefore:         preserve.BytesBefore + 
legacy.BytesBefore,
+                               BytesAfter:          preserve.BytesAfter + 
legacy.BytesAfter,
+                       }, nil
+               }
+       }
+
        // Preserve row lineage only when every source file in the group carries

Review Comment:
     This comment lost its tail when the old degradation block was removed — it 
now ends mid-sentence at "carries". Worth completing or trimming it.
   



##########
table/rewrite_data_files.go:
##########
@@ -300,38 +300,61 @@ func ExecuteCompactionGroup(ctx context.Context, tbl 
*Table, group CompactionTas
                scanOpts = append(scanOpts, 
WitMaxConcurrency(cfg.scanConcurrency))
        }
 
+       if tbl.metadata.Version() >= 3 {
+               lineageTasks, legacyTasks := splitTasksByLineage(group.Tasks)
+               if len(lineageTasks) > 0 && len(legacyTasks) > 0 {
+                       slog.Info("compaction group has mixed row-lineage 
tasks; splitting to preserve _row_id for lineage-capable files",
+                               "partition_key", group.PartitionKey,
+                               "lineage_tasks", len(lineageTasks),
+                               "legacy_tasks", len(legacyTasks))
+
+                       lineageGroup := CompactionTaskGroup{
+                               PartitionKey:   group.PartitionKey,
+                               Tasks:          lineageTasks,
+                               TotalSizeBytes: sumTaskBytes(lineageTasks),
+                       }
+                       legacyGroup := CompactionTaskGroup{
+                               PartitionKey:   group.PartitionKey,
+                               Tasks:          legacyTasks,
+                               TotalSizeBytes: sumTaskBytes(legacyTasks),
+                       }
+
+                       preserve, err := executeCompactionTaskGroup(ctx, tbl, 
scanOpts, lineageGroup, cfg.targetFileSize, true)
+                       if err != nil {
+                               return CompactionGroupResult{}, err
+                       }
+                       legacy, err := executeCompactionTaskGroup(ctx, tbl, 
scanOpts, legacyGroup, cfg.targetFileSize, false)
+                       if err != nil {
+                               return CompactionGroupResult{}, err
+                       }
+
+                       safePosDeletes := 
mergeTaskFiles(preserve.SafePosDeletes, legacy.SafePosDeletes)
+                       safeDeletionVectors := 
mergeTaskFiles(preserve.SafeDeletionVectors, legacy.SafeDeletionVectors)

Review Comment:
   `mergeTaskFiles` dedups by `df.FilePath()`, which is the Puffin path — but a 
single Puffin file can hold DV blobs for multiple data files, so DVs have to be 
deduped by `ReferencedDataFile()`, not path. That's the invariant the rest of 
the codebase already relies on (see `readAllDeletionVectors` keying by 
referenced data file, and `deleteFileRemoved`/`removeDeletionVector` in 
snapshot_producers.go using `deletedDVsByRef`).
     
     Since the split sends lineage-capable and legacy data files into different 
subgroups, a shared Puffin holding one DV blob for a lineage file and one for a 
legacy file produces two entries with the same `FilePath()` but different 
`ReferencedDataFile()`. This merge collapses them to one, so the dropped DV 
never reaches `ApplyResult` — its data file gets rewritten while its DV is left 
orphaned (stale delete referencing a removed file). This bites on tables  whose 
DVs were written by Java/Spark, which bundle blobs. 
     
     Pos-deletes are fine to dedup by path, so I'd suggest a separate DV merge 
keyed by referenced data file (mirroring `CollectSafeDeletionVectors`) rather 
than reusing the path-based helper for both. Could you also add a mixed-group 
case where a single DV Puffin references one lineage and one legacy data file, 
and assert both DVs are expunged?



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to