This is an automated email from the ASF dual-hosted git repository.

JingsongLi pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/paimon.git


The following commit(s) were added to refs/heads/master by this push:
     new 94af1b2cc1 [core] Support branch creation from a tag whose snapshot 
has expired (#8748)
94af1b2cc1 is described below

commit 94af1b2cc174f2051dc14ef1c761ca7b1d0a3308
Author: Arnav Balyan <[email protected]>
AuthorDate: Tue Jul 21 11:29:20 2026 +0530

    [core] Support branch creation from a tag whose snapshot has expired (#8748)
---
 .../paimon/utils/FileSystemBranchManager.java      | 14 ++++++---
 .../apache/paimon/table/SimpleTableTestBase.java   | 36 ++++++++++++++++++++++
 2 files changed, 46 insertions(+), 4 deletions(-)

diff --git 
a/paimon-core/src/main/java/org/apache/paimon/utils/FileSystemBranchManager.java
 
b/paimon-core/src/main/java/org/apache/paimon/utils/FileSystemBranchManager.java
index aee1787850..9466ccd851 100644
--- 
a/paimon-core/src/main/java/org/apache/paimon/utils/FileSystemBranchManager.java
+++ 
b/paimon-core/src/main/java/org/apache/paimon/utils/FileSystemBranchManager.java
@@ -123,10 +123,16 @@ public class FileSystemBranchManager implements 
BranchManager {
                     tagManager.tagPath(tagName),
                     tagManager.copyWithBranch(branchName).tagPath(tagName),
                     true);
-            fileIO.copyFile(
-                    snapshotManager.snapshotPath(snapshot.id()),
-                    
snapshotManager.copyWithBranch(branchName).snapshotPath(snapshot.id()),
-                    true);
+            Path branchSnapshotPath =
+                    
snapshotManager.copyWithBranch(branchName).snapshotPath(snapshot.id());
+            if (snapshotManager.snapshotExists(snapshot.id())) {
+                fileIO.copyFile(
+                        snapshotManager.snapshotPath(snapshot.id()), 
branchSnapshotPath, true);
+            } else {
+                // The snapshot may have expired while the tag preserved its 
content, so write the
+                // snapshot from the tag instead of copying a file that no 
longer exists.
+                fileIO.writeFile(branchSnapshotPath, snapshot.toJson(), true);
+            }
             copySchemasToBranch(branchName, snapshot.schemaId());
         } catch (IOException e) {
             throw new RuntimeException(
diff --git 
a/paimon-core/src/test/java/org/apache/paimon/table/SimpleTableTestBase.java 
b/paimon-core/src/test/java/org/apache/paimon/table/SimpleTableTestBase.java
index e02c2bae1b..8c7266939f 100644
--- a/paimon-core/src/test/java/org/apache/paimon/table/SimpleTableTestBase.java
+++ b/paimon-core/src/test/java/org/apache/paimon/table/SimpleTableTestBase.java
@@ -1105,6 +1105,42 @@ public abstract class SimpleTableTestBase {
         assertThat(branchSchema.equals(schema0)).isTrue();
     }
 
+    @Test
+    public void testCreateBranchFromTagAfterSnapshotExpired() throws Exception 
{
+        FileStoreTable table = createFileStoreTable();
+        try (StreamTableWrite write = table.newWrite(commitUser);
+                StreamTableCommit commit = table.newCommit(commitUser)) {
+            write.write(rowData(1, 10, 100L));
+            commit.commit(0, write.prepareCommit(false, 1));
+            write.write(rowData(2, 20, 200L));
+            commit.commit(1, write.prepareCommit(false, 2));
+            write.write(rowData(3, 30, 300L));
+            commit.commit(2, write.prepareCommit(false, 3));
+        }
+
+        table.createTag("test-tag", 2);
+
+        Options expire = new Options();
+        expire.set(SNAPSHOT_NUM_RETAINED_MIN, 1);
+        expire.set(SNAPSHOT_NUM_RETAINED_MAX, 1);
+        table.copy(expire.toMap()).newCommit(commitUser).expireSnapshots();
+
+        SnapshotManager snapshotManager = newSnapshotManager(table.fileIO(), 
table.location());
+        assertThat(snapshotManager.snapshotExists(2)).isFalse();
+        TagManager tagManager = new TagManager(table.fileIO(), 
table.location());
+        assertThat(tagManager.tagExists("test-tag")).isTrue();
+
+        table.createBranch("test-branch", "test-tag");
+        assertThat(table.branchManager().branchExists("test-branch")).isTrue();
+
+        Snapshot tagged = tagManager.getOrThrow("test-tag").trimToSnapshot();
+        SnapshotManager branchSnapshotManager =
+                newSnapshotManager(table.fileIO(), table.location(), 
"test-branch");
+        Snapshot branchSnapshot =
+                SnapshotManager.fromPath(table.fileIO(), 
branchSnapshotManager.snapshotPath(2));
+        assertThat(branchSnapshot.equals(tagged)).isTrue();
+    }
+
     @Test
     public void testUnsupportedBranchName() throws Exception {
         FileStoreTable table = createFileStoreTable();

Reply via email to