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 5d8110d660 [core] Preserve reused compaction files when aborting 
commits (#10582)
5d8110d660 is described below

commit 5d8110d6600221f7cdb23a0ba618115e5eec2014
Author: Luo Zhengjie <[email protected]>
AuthorDate: Sun Oct 18 16:57:34 2026 +0800

    [core] Preserve reused compaction files when aborting commits (#10582)
---
 .../paimon/operation/FileStoreCommitImpl.java      |  15 ++-
 .../paimon/operation/FileStoreCommitTest.java      | 101 +++++++++++++++++++++
 2 files changed, 115 insertions(+), 1 deletion(-)

diff --git 
a/paimon-core/src/main/java/org/apache/paimon/operation/FileStoreCommitImpl.java
 
b/paimon-core/src/main/java/org/apache/paimon/operation/FileStoreCommitImpl.java
index 2123517e13..ffe79cf4bb 100644
--- 
a/paimon-core/src/main/java/org/apache/paimon/operation/FileStoreCommitImpl.java
+++ 
b/paimon-core/src/main/java/org/apache/paimon/operation/FileStoreCommitImpl.java
@@ -27,6 +27,7 @@ import org.apache.paimon.data.BinaryRow;
 import org.apache.paimon.data.InternalRow;
 import org.apache.paimon.disk.IOManager;
 import org.apache.paimon.fs.FileIO;
+import org.apache.paimon.fs.Path;
 import org.apache.paimon.index.IndexFileMeta;
 import org.apache.paimon.index.IndexPathFactory;
 import org.apache.paimon.io.DataFileMeta;
@@ -725,13 +726,25 @@ public class FileStoreCommitImpl implements 
FileStoreCommit {
             List<DataFileMeta> dataFilesToDelete = new ArrayList<>();
             
dataFilesToDelete.addAll(commitMessage.newFilesIncrement().newFiles());
             
dataFilesToDelete.addAll(commitMessage.newFilesIncrement().changelogFiles());
-            
dataFilesToDelete.addAll(commitMessage.compactIncrement().compactAfter());
             
dataFilesToDelete.addAll(commitMessage.compactIncrement().changelogFiles());
 
+            // New files may also be compaction inputs, but abort still owns 
their cleanup.
             for (DataFileMeta file : dataFilesToDelete) {
                 
file.collectFiles(dataPathFactory).forEach(fileIO::deleteQuietly);
             }
 
+            // A level upgrade may reuse a main file while adding new 
auxiliary files.
+            Set<Path> compactInputPaths =
+                    commitMessage.compactIncrement().compactBefore().stream()
+                            .flatMap(file -> 
file.collectFiles(dataPathFactory).stream())
+                            .collect(Collectors.toSet());
+
+            for (DataFileMeta file : 
commitMessage.compactIncrement().compactAfter()) {
+                file.collectFiles(dataPathFactory).stream()
+                        .filter(path -> !compactInputPaths.contains(path))
+                        .forEach(fileIO::deleteQuietly);
+            }
+
             List<IndexFileMeta> indexFilesToDelete = new ArrayList<>();
             
indexFilesToDelete.addAll(commitMessage.newFilesIncrement().newIndexFiles());
             
indexFilesToDelete.addAll(commitMessage.compactIncrement().newIndexFiles());
diff --git 
a/paimon-core/src/test/java/org/apache/paimon/operation/FileStoreCommitTest.java
 
b/paimon-core/src/test/java/org/apache/paimon/operation/FileStoreCommitTest.java
index f4501aacce..99fd1f6532 100644
--- 
a/paimon-core/src/test/java/org/apache/paimon/operation/FileStoreCommitTest.java
+++ 
b/paimon-core/src/test/java/org/apache/paimon/operation/FileStoreCommitTest.java
@@ -1224,6 +1224,107 @@ public class FileStoreCommitTest {
         }
     }
 
+    @ParameterizedTest
+    @ValueSource(booleans = {false, true})
+    public void testAbortPreservesReusedCompactionFiles(boolean external) 
throws Exception {
+        TestAppendFileStore store = 
TestAppendFileStore.createAppendStore(tempDir, new HashMap<>());
+        BinaryRow partition = gen.getPartition(gen.next());
+        DataFilePathFactory pathFactory =
+                store.pathFactory().createDataFilePathFactory(partition, 0);
+        Path compactNewPath = pathFactory.newPath();
+        // External files may share a name with a different physical file in 
the bucket directory.
+        Path reusedPath =
+                external
+                        ? new 
Path(tempDir.resolve(compactNewPath.getName()).toUri())
+                        : pathFactory.newPath();
+        DataFileMeta reused = createDataFileWithExtraFile(store, reusedPath, 
external);
+        DataFileMeta compactNew = createDataFileWithExtraFile(store, 
compactNewPath, false);
+        Path newLookupPath = new Path(reusedPath.getParent(), 
"new-lookup.sst");
+        store.fileIO().newOutputStream(newLookupPath, false).close();
+        List<String> upgradedExtraFiles = new ArrayList<>(reused.extraFiles());
+        upgradedExtraFiles.add(newLookupPath.getName());
+        DataFileMeta upgraded = reused.upgrade(1).copy(upgradedExtraFiles);
+        CommitMessage commitMessage =
+                new CommitMessageImpl(
+                        partition,
+                        0,
+                        store.options().bucket(),
+                        new DataIncrement(
+                                Collections.emptyList(),
+                                Collections.emptyList(),
+                                Collections.emptyList()),
+                        new CompactIncrement(
+                                Collections.singletonList(reused),
+                                Arrays.asList(upgraded, compactNew),
+                                Collections.emptyList()));
+
+        try (FileStoreCommitImpl commit = store.newCommit()) {
+            commit.abort(Collections.singletonList(commitMessage));
+            commit.abort(Collections.singletonList(commitMessage));
+        }
+
+        for (Path path : reused.collectFiles(pathFactory)) {
+            assertThat(store.fileIO().exists(path)).as("reused file %s", 
path).isTrue();
+        }
+        for (Path path : compactNew.collectFiles(pathFactory)) {
+            assertThat(store.fileIO().exists(path)).as("new compaction file 
%s", path).isFalse();
+        }
+        assertThat(store.fileIO().exists(newLookupPath))
+                .as("new lookup file %s", newLookupPath)
+                .isFalse();
+    }
+
+    @ParameterizedTest
+    @ValueSource(strings = {"NONE", "INPUT", "FULL_COMPACTION"})
+    public void testAbortCleansUncommittedCompactionInputs(String 
changelogProducer)
+            throws Exception {
+        TestFileStore store =
+                createStore(false, 1, 
CoreOptions.ChangelogProducer.valueOf(changelogProducer));
+        KeyValue first = gen.nextInsert("20201110", 10, 1L, new int[] {1, 1}, 
"first");
+        BinaryRow partition = gen.getPartition(first);
+        DataFilePathFactory pathFactory =
+                store.pathFactory().createDataFilePathFactory(partition, 0);
+
+        AbstractFileStoreWrite<KeyValue> write = store.newWrite();
+        try (FileStoreCommitImpl commit = store.newCommit()) {
+            write.write(partition, 0, first);
+            write.compact(partition, 0, false);
+            write.write(partition, 0, first);
+            write.compact(partition, 0, true);
+            List<CommitMessage> messages = write.prepareCommit(true, 0);
+            assertThat(messages).hasSize(1);
+            CommitMessageImpl message = (CommitMessageImpl) messages.get(0);
+            assertThat(message.newFilesIncrement().newFiles()).hasSize(2);
+            assertThat(message.compactIncrement().compactBefore())
+                    
.containsExactlyInAnyOrderElementsOf(message.newFilesIncrement().newFiles());
+            assertThat(message.compactIncrement().compactAfter()).hasSize(1);
+
+            List<DataFileMeta> pendingFiles =
+                    new ArrayList<>(message.newFilesIncrement().newFiles());
+            pendingFiles.addAll(message.newFilesIncrement().changelogFiles());
+            pendingFiles.addAll(message.compactIncrement().compactAfter());
+            pendingFiles.addAll(message.compactIncrement().changelogFiles());
+            List<Path> pendingPaths =
+                    pendingFiles.stream()
+                            .flatMap(file -> 
file.collectFiles(pathFactory).stream())
+                            .collect(Collectors.toList());
+            for (Path path : pendingPaths) {
+                assertThat(store.fileIO().exists(path)).as("pending file %s", 
path).isTrue();
+            }
+            assertThat(store.snapshotManager().latestSnapshotId()).isNull();
+
+            commit.abort(messages);
+            commit.abort(messages);
+
+            for (Path path : pendingPaths) {
+                assertThat(store.fileIO().exists(path)).as("aborted file %s", 
path).isFalse();
+            }
+            assertThat(store.snapshotManager().latestSnapshotId()).isNull();
+        } finally {
+            write.close();
+        }
+    }
+
     private static DataFileMeta createDataFileWithExtraFile(
             TestAppendFileStore store, Path path, boolean external) throws 
Exception {
         store.fileIO().newOutputStream(path, false).close();

Reply via email to