JingsongLi commented on code in PR #10897:
URL: https://github.com/apache/paimon/pull/10897#discussion_r4435419296


##########
paimon-core/src/main/java/org/apache/paimon/table/AbstractFileStoreTable.java:
##########
@@ -507,7 +516,131 @@ public ExpireSnapshots newExpireSnapshots() {
                 changelogManager(),
                 store().newSnapshotDeletion(),
                 store().newTagManager(),
-                store().options().scanManifestParallelism());
+                store().options().scanManifestParallelism(),
+                newOtherBranchesLiveFiles());
+    }
+
+    /**
+     * Builds a collector for data files referenced by branches other than the 
one being expired.
+     *
+     * <p>Every branch shares the same physical data directory, so expiring 
snapshots on one branch
+     * must not delete files another branch (for example main) still 
references. Returns {@code
+     * null} when there is no other branch, keeping the common single-branch 
path untouched.
+     */
+    @Nullable
+    private ExpireSnapshotsImpl.OtherBranchesLiveFiles 
newOtherBranchesLiveFiles() {
+        String currentBranch = BranchManager.normalizeBranch(currentBranch());
+        List<String> otherBranches;
+        try {
+            List<String> allBranches = new 
ArrayList<>(branchManager().branches());

Review Comment:
   [P1] Discover branches for each expiration, not only when constructing the 
expirer. `newExpireRunnable()` creates this object once and reuses it for every 
commit. If a streaming writer starts before a branch is created, this code 
returns null (or captures a list omitting that branch), so that branch is never 
protected by the running writer. Reproduced on an append table: write snapshot 
1, create a tag, cache `newExpireSnapshots()`, create a branch from the tag, 
remove the main tag, append snapshot 2, expire to one snapshot; reading the 
branch fails with FileNotFoundException for its manifest list. Constructing the 
expirer after branch creation passes the identical control. Refresh the branch 
inventory within each expiry batch, including the initially single-branch case.



##########
paimon-core/src/main/java/org/apache/paimon/table/AbstractFileStoreTable.java:
##########
@@ -507,7 +516,131 @@ public ExpireSnapshots newExpireSnapshots() {
                 changelogManager(),
                 store().newSnapshotDeletion(),
                 store().newTagManager(),
-                store().options().scanManifestParallelism());
+                store().options().scanManifestParallelism(),
+                newOtherBranchesLiveFiles());
+    }
+
+    /**
+     * Builds a collector for data files referenced by branches other than the 
one being expired.
+     *
+     * <p>Every branch shares the same physical data directory, so expiring 
snapshots on one branch
+     * must not delete files another branch (for example main) still 
references. Returns {@code
+     * null} when there is no other branch, keeping the common single-branch 
path untouched.
+     */
+    @Nullable
+    private ExpireSnapshotsImpl.OtherBranchesLiveFiles 
newOtherBranchesLiveFiles() {
+        String currentBranch = BranchManager.normalizeBranch(currentBranch());
+        List<String> otherBranches;
+        try {
+            List<String> allBranches = new 
ArrayList<>(branchManager().branches());
+            allBranches.add(Identifier.DEFAULT_MAIN_BRANCH);
+            otherBranches =
+                    allBranches.stream()
+                            .map(BranchManager::normalizeBranch)
+                            .distinct()
+                            .filter(branch -> !branch.equals(currentBranch))
+                            .collect(Collectors.toList());
+        } catch (Exception e) {
+            LOG.warn(
+                    "Failed to list branches before snapshot expiration. "
+                            + "Cross-branch data file protection is disabled 
for this run.",
+                    e);
+            return null;

Review Comment:
   [P1] Retain the batch when branch discovery fails. Returning null here 
selects unprotected deletion, contrary to the stated fail-safe behavior; 
because the expirer is reused, even a transient listing error disables 
protection for its lifetime. FileSystemBranchManager.branches() can throw for a 
filesystem listing failure. In an actual append-table/tag/branch fixture, 
injecting that listing failure before constructing the expirer lets main 
expiration delete the branch's manifest list and the branch read fails. 
Propagate the failure into the existing keep-batch path (and retry discovery 
next run), instead of treating an unknown branch set as no branches.



##########
paimon-core/src/main/java/org/apache/paimon/table/AbstractFileStoreTable.java:
##########
@@ -507,7 +516,131 @@ public ExpireSnapshots newExpireSnapshots() {
                 changelogManager(),
                 store().newSnapshotDeletion(),
                 store().newTagManager(),
-                store().options().scanManifestParallelism());
+                store().options().scanManifestParallelism(),
+                newOtherBranchesLiveFiles());

Review Comment:
   [P1] Apply shared-file protection to the deferred changelog cleanup too. 
With `changelog-producer=none` and changelog retention greater than snapshot 
retention, append-table deletion is deferred to 
ExpireChangelogImpl/ChangelogDeletion, but this collector is only installed on 
snapshot expiration. Reproduced using supported retention options: snapshot 1 
has A and is branched; overwrite main to B, append snapshots 3/4, remove the 
main tag, then expire snapshots. The branch still reads A at this point. 
Expiring changelogs [1,3) then deletes its referenced manifest list and the 
same branch read fails with FileNotFoundException. This also runs after 
snapshot expiration in `newExpireRunnable()`. Share the cross-branch data and 
metadata skipping sets with changelog expiration, and add a decoupled-lifecycle 
regression case.



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

Reply via email to