laskoviymishka commented on code in PR #2667:
URL: https://github.com/apache/iceberg-rust/pull/2667#discussion_r3463146654


##########
crates/iceberg/src/transaction/expire_snapshots.rs:
##########
@@ -321,6 +321,26 @@ impl TransactionAction for ExpireSnapshotsAction {
             .into_iter()
             .map(|ref_name| TableUpdate::RemoveSnapshotRef { ref_name })
             .collect();
+
+        // Drop the statistics metadata tied to each expired snapshot, 
mirroring Java
+        // `RemoveSnapshots.removeSnapshots`. This only rewrites metadata; the 
puffin files those
+        // entries point at are deleted by the higher-level file-cleanup 
maintenance operation.
+        for snapshot_id in &plan.ids_to_remove {
+            if metadata.statistics_for_snapshot(*snapshot_id).is_some() {

Review Comment:
   The three new tests all expire exactly one snapshot via 
`retain_last`/`expire_older_than_ms`. Two cases I'd like to see (follow-up is 
fine): one where a snapshot is expired purely because its sole ref aged out 
(extend `test_ref_aging_drops_old_tag_and_expires_its_snapshot` with 
`stats_file(1)` and assert `removed_statistics == vec![1]`), and a 3-snapshot 
chain where two expire carrying stats, asserting `removed_statistics == vec![1, 
2]`.
   
   The ref-aging path is the one I most want covered — it reaches this loop 
through a different `plan()` branch than the retain/expire-older cases.



##########
crates/iceberg/src/transaction/expire_snapshots.rs:
##########
@@ -321,6 +321,26 @@ impl TransactionAction for ExpireSnapshotsAction {
             .into_iter()
             .map(|ref_name| TableUpdate::RemoveSnapshotRef { ref_name })
             .collect();
+
+        // Drop the statistics metadata tied to each expired snapshot, 
mirroring Java
+        // `RemoveSnapshots.removeSnapshots`. This only rewrites metadata; the 
puffin files those
+        // entries point at are deleted by the higher-level file-cleanup 
maintenance operation.
+        for snapshot_id in &plan.ids_to_remove {

Review Comment:
   Minor, but binding `for &snapshot_id in &plan.ids_to_remove` drops the 
repeated `*snapshot_id` derefs at the use sites and reads a bit cleaner. clippy 
may nudge on this too.



##########
crates/iceberg/src/transaction/expire_snapshots.rs:
##########
@@ -472,6 +493,69 @@ mod tests {
         base.with_metadata(Arc::new(builder.build().unwrap().metadata))
     }
 
+    /// Like [`table_with`], but also attaches statistics and 
partition-statistics files.
+    fn table_with_stats(

Review Comment:
   This duplicates the snapshot/ref wiring from `table_with` verbatim. I'd 
build on top of `table_with` (or share the snapshot/ref loop) and just layer 
the stats on after, so the two don't silently diverge if that wiring changes 
later.



##########
crates/iceberg/src/transaction/expire_snapshots.rs:
##########
@@ -321,6 +321,26 @@ impl TransactionAction for ExpireSnapshotsAction {
             .into_iter()
             .map(|ref_name| TableUpdate::RemoveSnapshotRef { ref_name })
             .collect();
+
+        // Drop the statistics metadata tied to each expired snapshot, 
mirroring Java
+        // `RemoveSnapshots.removeSnapshots`. This only rewrites metadata; the 
puffin files those
+        // entries point at are deleted by the higher-level file-cleanup 
maintenance operation.
+        for snapshot_id in &plan.ids_to_remove {
+            if metadata.statistics_for_snapshot(*snapshot_id).is_some() {
+                updates.push(TableUpdate::RemoveStatistics {
+                    snapshot_id: *snapshot_id,
+                });
+            }
+            if metadata
+                .partition_statistics_for_snapshot(*snapshot_id)
+                .is_some()
+            {
+                updates.push(TableUpdate::RemovePartitionStatistics {
+                    snapshot_id: *snapshot_id,
+                });
+            }
+        }

Review Comment:
   We end up emitting the stats removals between the `RemoveSnapshotRef` 
updates and the `RemoveSnapshots` push, so the sequence reads 
`[RemoveSnapshotRef*, RemoveStatistics*, RemovePartitionStatistics*, 
RemoveSnapshots]`. It's correct since these touch disjoint maps, but I'd find 
it cleaner to read as refs → snapshots → stats, and it's a touch safer against 
a strict server that validates updates in sequence.
   
   Moving this loop to after the `RemoveSnapshots` push gets that ordering for 
free — fine as a follow-up. wdyt?



-- 
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