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 c638a4d427 [core] Extract the shared incremental clustering rewriter 
(#10679)
c638a4d427 is described below

commit c638a4d42756c84c14d1e503f23f024d05463930
Author: Jingsong Lee <[email protected]>
AuthorDate: Tue Oct 20 12:39:08 2026 +0800

    [core] Extract the shared incremental clustering rewriter (#10679)
---
 .../append/cluster/HistoryPartitionCluster.java    |  15 ++-
 .../append/cluster/IncrementalClusterManager.java  | 101 +++--------------
 .../append/cluster/IncrementalClusterRewriter.java | 123 +++++++++++++++++++++
 .../cluster/HistoryPartitionClusterTest.java       |  10 +-
 .../cluster/IncrementalClusterManagerTest.java     |  44 +++++---
 .../flink/compact/IncrementalClusterCompact.java   |   4 +-
 .../paimon/spark/procedure/CompactProcedure.java   |   3 +-
 7 files changed, 190 insertions(+), 110 deletions(-)

diff --git 
a/paimon-core/src/main/java/org/apache/paimon/append/cluster/HistoryPartitionCluster.java
 
b/paimon-core/src/main/java/org/apache/paimon/append/cluster/HistoryPartitionCluster.java
index 8acbb8e778..d7d5081366 100644
--- 
a/paimon-core/src/main/java/org/apache/paimon/append/cluster/HistoryPartitionCluster.java
+++ 
b/paimon-core/src/main/java/org/apache/paimon/append/cluster/HistoryPartitionCluster.java
@@ -44,9 +44,9 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
+import java.util.function.Function;
 import java.util.stream.Collectors;
 
-import static 
org.apache.paimon.append.cluster.IncrementalClusterManager.constructPartitionLevels;
 import static 
org.apache.paimon.append.cluster.IncrementalClusterManager.logForPartitionLevel;
 
 /** Handle historical partition for full clustering. */
@@ -62,6 +62,7 @@ public class HistoryPartitionCluster {
     private final Duration historyPartitionIdleTime;
     private final int historyPartitionLimit;
     private final int maxLevel;
+    private final Function<List<DataFileMeta>, List<LevelSortedRun>> 
levelConstructor;
 
     public HistoryPartitionCluster(
             FileStoreTable table,
@@ -70,7 +71,8 @@ public class HistoryPartitionCluster {
             InternalRowPartitionComputer partitionComputer,
             PartitionPredicate specifiedPartitions,
             Duration historyPartitionIdleTime,
-            int historyPartitionLimit) {
+            int historyPartitionLimit,
+            Function<List<DataFileMeta>, List<LevelSortedRun>> 
levelConstructor) {
         this.table = table;
         this.snapshot = snapshot;
         this.incrementalClusterStrategy = incrementalClusterStrategy;
@@ -79,6 +81,7 @@ public class HistoryPartitionCluster {
         this.historyPartitionIdleTime = historyPartitionIdleTime;
         this.historyPartitionLimit = historyPartitionLimit;
         this.maxLevel = table.coreOptions().numLevels() - 1;
+        this.levelConstructor = levelConstructor;
     }
 
     @Nullable
@@ -87,7 +90,8 @@ public class HistoryPartitionCluster {
             Snapshot snapshot,
             IncrementalClusterStrategy incrementalClusterStrategy,
             InternalRowPartitionComputer partitionComputer,
-            @Nullable PartitionPredicate specifiedPartitions) {
+            @Nullable PartitionPredicate specifiedPartitions,
+            Function<List<DataFileMeta>, List<LevelSortedRun>> 
levelConstructor) {
         if (table.schema().partitionKeys().isEmpty()) {
             return null;
         }
@@ -108,7 +112,8 @@ public class HistoryPartitionCluster {
                 partitionComputer,
                 specifiedPartitions,
                 idleTime,
-                limit);
+                limit,
+                levelConstructor);
     }
 
     public Map<BinaryRow, CompactUnit> pickForHistoryPartitions() {
@@ -163,7 +168,7 @@ public class HistoryPartitionCluster {
                 .collect(
                         Collectors.toMap(
                                 Map.Entry::getKey,
-                                entry -> 
constructPartitionLevels(entry.getValue())));
+                                entry -> 
levelConstructor.apply(entry.getValue())));
     }
 
     private Map<BinaryRow, List<DataFileMeta>> filterPartitions(
diff --git 
a/paimon-core/src/main/java/org/apache/paimon/append/cluster/IncrementalClusterManager.java
 
b/paimon-core/src/main/java/org/apache/paimon/append/cluster/IncrementalClusterManager.java
index f5642f6648..2fa240e028 100644
--- 
a/paimon-core/src/main/java/org/apache/paimon/append/cluster/IncrementalClusterManager.java
+++ 
b/paimon-core/src/main/java/org/apache/paimon/append/cluster/IncrementalClusterManager.java
@@ -25,21 +25,13 @@ import org.apache.paimon.compact.CompactUnit;
 import org.apache.paimon.data.BinaryRow;
 import org.apache.paimon.deletionvectors.append.AppendDeleteFileMaintainer;
 import org.apache.paimon.deletionvectors.append.BaseAppendDeleteFileMaintainer;
-import org.apache.paimon.index.IndexFileMeta;
-import org.apache.paimon.io.CompactIncrement;
 import org.apache.paimon.io.DataFileMeta;
-import org.apache.paimon.io.DataIncrement;
-import org.apache.paimon.manifest.FileKind;
-import org.apache.paimon.manifest.IndexManifestEntry;
 import org.apache.paimon.mergetree.LevelSortedRun;
-import org.apache.paimon.mergetree.SortedRun;
 import org.apache.paimon.partition.PartitionPredicate;
 import org.apache.paimon.table.BucketMode;
 import org.apache.paimon.table.FileStoreTable;
 import org.apache.paimon.table.sink.CommitMessage;
-import org.apache.paimon.table.sink.CommitMessageImpl;
 import org.apache.paimon.table.source.DataSplit;
-import org.apache.paimon.table.source.DeletionFile;
 import org.apache.paimon.table.source.SplitGenerator;
 import org.apache.paimon.table.source.snapshot.SnapshotReader;
 import org.apache.paimon.utils.InternalRowPartitionComputer;
@@ -51,8 +43,6 @@ import org.slf4j.LoggerFactory;
 import javax.annotation.Nullable;
 
 import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -71,18 +61,17 @@ public class IncrementalClusterManager {
     private final InternalRowPartitionComputer partitionComputer;
     private final Snapshot snapshot;
     private final SnapshotReader snapshotReader;
+    private final IncrementalClusterRewriter rewriter;
     private final IncrementalClusterStrategy incrementalClusterStrategy;
     private final CoreOptions.OrderType clusterCurve;
     private final List<String> clusterKeys;
     private final int numLevels;
     private final @Nullable HistoryPartitionCluster historyPartitionCluster;
 
-    public IncrementalClusterManager(FileStoreTable table) {
-        this(table, null);
-    }
-
     public IncrementalClusterManager(
-            FileStoreTable table, @Nullable PartitionPredicate 
specifiedPartitions) {
+            FileStoreTable table,
+            @Nullable PartitionPredicate specifiedPartitions,
+            Snapshot snapshot) {
         checkArgument(
                 table.bucketMode() == BucketMode.BUCKET_UNAWARE,
                 "only append unaware-bucket table support incremental 
clustering.");
@@ -99,7 +88,8 @@ public class IncrementalClusterManager {
                         table.store().partitionType(),
                         table.partitionKeys().toArray(new String[0]),
                         table.coreOptions().legacyPartitionName());
-        this.snapshot = table.snapshotManager().latestSnapshot();
+        this.snapshot = snapshot;
+        this.rewriter = IncrementalClusterRewriter.create(table, snapshot);
         this.snapshotReader =
                 table.newSnapshotReader()
                         .dropStats()
@@ -120,7 +110,8 @@ public class IncrementalClusterManager {
                         snapshot,
                         incrementalClusterStrategy,
                         partitionComputer,
-                        specifiedPartitions);
+                        specifiedPartitions,
+                        rewriter::constructPartitionLevels);
     }
 
     public Map<BinaryRow, CompactUnit> createCompactUnits(boolean 
fullCompaction) {
@@ -177,33 +168,7 @@ public class IncrementalClusterManager {
                 .collect(
                         Collectors.toMap(
                                 Map.Entry::getKey,
-                                entry -> 
constructPartitionLevels(entry.getValue())));
-    }
-
-    public static List<LevelSortedRun> 
constructPartitionLevels(List<DataFileMeta> partitionFiles) {
-        List<LevelSortedRun> partitionLevels = new ArrayList<>();
-        Map<Integer, List<DataFileMeta>> levelMap =
-                
partitionFiles.stream().collect(Collectors.groupingBy(DataFileMeta::level));
-
-        for (Map.Entry<Integer, List<DataFileMeta>> entry : 
levelMap.entrySet()) {
-            int level = entry.getKey();
-            if (level == 0) {
-                for (DataFileMeta level0File : entry.getValue()) {
-                    partitionLevels.add(
-                            new LevelSortedRun(level, 
SortedRun.fromSingle(level0File)));
-                }
-            } else {
-                // don't need to guarantee that the files within the same 
sorted run are
-                // non-overlapping here, so we call SortedRun.fromSorted() to 
avoid sorting and
-                // validation
-                partitionLevels.add(
-                        new LevelSortedRun(level, 
SortedRun.fromSorted(entry.getValue())));
-            }
-        }
-
-        // sort by level
-        partitionLevels.sort(Comparator.comparing(LevelSortedRun::level));
-        return partitionLevels;
+                                entry -> 
rewriter.constructPartitionLevels(entry.getValue())));
     }
 
     public Map<BinaryRow, Pair<List<DataSplit>, CommitMessage>> 
toSplitsAndRewriteDvFiles(
@@ -235,48 +200,14 @@ public class IncrementalClusterManager {
 
                 String bucketPath =
                         snapshotReader.pathFactory().bucketPath(partition, 
0).toString();
-                builder.withDataFiles(dataFiles)
-                        .rawConvertible(splitGroup.rawConvertible)
-                        .withBucketPath(bucketPath);
-
-                if (dvMaintainer != null) {
-                    List<DeletionFile> dataDeletionFiles = new ArrayList<>();
-                    for (DataFileMeta file : dataFiles) {
-                        DeletionFile deletionFile =
-                                
dvMaintainer.notifyRemovedDeletionVector(file.fileName());
-                        dataDeletionFiles.add(deletionFile);
-                    }
-                    builder.withDataDeletionFiles(dataDeletionFiles);
-                }
+                builder.withDataFiles(dataFiles).withBucketPath(bucketPath);
+
+                rewriter.configureSplit(builder, splitGroup, dvMaintainer);
                 splits.add(builder.build());
             }
 
-            // generate delete dv index meta
-            CommitMessage dvCommitMessage = null;
-            if (dvMaintainer != null) {
-                List<IndexFileMeta> newIndexFiles = new ArrayList<>();
-                List<IndexFileMeta> deletedIndexFiles = new ArrayList<>();
-                List<IndexManifestEntry> indexEntries = dvMaintainer.persist();
-                for (IndexManifestEntry entry : indexEntries) {
-                    if (entry.kind() == FileKind.ADD) {
-                        newIndexFiles.add(entry.indexFile());
-                    } else {
-                        deletedIndexFiles.add(entry.indexFile());
-                    }
-                }
-                dvCommitMessage =
-                        new CommitMessageImpl(
-                                dvMaintainer.getPartition(),
-                                0,
-                                table.coreOptions().bucket(),
-                                DataIncrement.emptyIncrement(),
-                                new CompactIncrement(
-                                        Collections.emptyList(),
-                                        Collections.emptyList(),
-                                        Collections.emptyList(),
-                                        newIndexFiles,
-                                        deletedIndexFiles));
-            }
+            CommitMessage dvCommitMessage =
+                    rewriter.finishPartition(dvMaintainer, 
table.coreOptions().bucket());
 
             result.put(partition, Pair.of(splits, dvCommitMessage));
         }
@@ -284,6 +215,10 @@ public class IncrementalClusterManager {
         return result;
     }
 
+    public List<CommitMessage> prepareCommitMessages(List<CommitMessage> 
messages) {
+        return rewriter.prepareCommitMessages(messages);
+    }
+
     public static List<DataFileMeta> upgrade(
             List<DataFileMeta> filesAfterCluster, int outputLevel) {
         return filesAfterCluster.stream()
diff --git 
a/paimon-core/src/main/java/org/apache/paimon/append/cluster/IncrementalClusterRewriter.java
 
b/paimon-core/src/main/java/org/apache/paimon/append/cluster/IncrementalClusterRewriter.java
new file mode 100644
index 0000000000..91fd178567
--- /dev/null
+++ 
b/paimon-core/src/main/java/org/apache/paimon/append/cluster/IncrementalClusterRewriter.java
@@ -0,0 +1,123 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.paimon.append.cluster;
+
+import org.apache.paimon.Snapshot;
+import org.apache.paimon.deletionvectors.append.AppendDeleteFileMaintainer;
+import org.apache.paimon.index.IndexFileMeta;
+import org.apache.paimon.io.CompactIncrement;
+import org.apache.paimon.io.DataFileMeta;
+import org.apache.paimon.io.DataIncrement;
+import org.apache.paimon.manifest.FileKind;
+import org.apache.paimon.manifest.IndexManifestEntry;
+import org.apache.paimon.mergetree.LevelSortedRun;
+import org.apache.paimon.mergetree.SortedRun;
+import org.apache.paimon.table.FileStoreTable;
+import org.apache.paimon.table.sink.CommitMessage;
+import org.apache.paimon.table.sink.CommitMessageImpl;
+import org.apache.paimon.table.source.DataSplit;
+import org.apache.paimon.table.source.SplitGenerator;
+
+import javax.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/** Table-specific file and deletion-vector semantics for incremental 
clustering. */
+public class IncrementalClusterRewriter {
+
+    public static IncrementalClusterRewriter create(FileStoreTable table, 
Snapshot snapshot) {
+        return new IncrementalClusterRewriter();
+    }
+
+    List<LevelSortedRun> constructPartitionLevels(List<DataFileMeta> 
partitionFiles) {
+        List<LevelSortedRun> partitionLevels = new ArrayList<>();
+        Map<Integer, List<DataFileMeta>> levelMap =
+                
partitionFiles.stream().collect(Collectors.groupingBy(DataFileMeta::level));
+
+        for (Map.Entry<Integer, List<DataFileMeta>> entry : 
levelMap.entrySet()) {
+            int level = entry.getKey();
+            if (level == 0) {
+                for (DataFileMeta level0File : entry.getValue()) {
+                    partitionLevels.add(
+                            new LevelSortedRun(level, 
SortedRun.fromSingle(level0File)));
+                }
+            } else {
+                // don't need to guarantee that the files within the same 
sorted run are
+                // non-overlapping here, so we call SortedRun.fromSorted() to 
avoid sorting and
+                // validation
+                partitionLevels.add(
+                        new LevelSortedRun(level, 
SortedRun.fromSorted(entry.getValue())));
+            }
+        }
+
+        // sort by level
+        partitionLevels.sort(Comparator.comparing(LevelSortedRun::level));
+        return partitionLevels;
+    }
+
+    void configureSplit(
+            DataSplit.Builder builder,
+            SplitGenerator.SplitGroup splitGroup,
+            @Nullable AppendDeleteFileMaintainer dvMaintainer) {
+        builder.rawConvertible(splitGroup.rawConvertible);
+        if (dvMaintainer != null) {
+            builder.withDataDeletionFiles(
+                    splitGroup.files.stream()
+                            .map(file -> 
dvMaintainer.notifyRemovedDeletionVector(file.fileName()))
+                            .collect(Collectors.toList()));
+        }
+    }
+
+    @Nullable
+    CommitMessage finishPartition(
+            @Nullable AppendDeleteFileMaintainer dvMaintainer, int 
totalBuckets) {
+        if (dvMaintainer == null) {
+            return null;
+        }
+        List<IndexFileMeta> newIndexFiles = new ArrayList<>();
+        List<IndexFileMeta> deletedIndexFiles = new ArrayList<>();
+        for (IndexManifestEntry entry : dvMaintainer.persist()) {
+            if (entry.kind() == FileKind.ADD) {
+                newIndexFiles.add(entry.indexFile());
+            } else {
+                deletedIndexFiles.add(entry.indexFile());
+            }
+        }
+        return new CommitMessageImpl(
+                dvMaintainer.getPartition(),
+                0,
+                totalBuckets,
+                DataIncrement.emptyIncrement(),
+                new CompactIncrement(
+                        Collections.emptyList(),
+                        Collections.emptyList(),
+                        Collections.emptyList(),
+                        newIndexFiles,
+                        deletedIndexFiles));
+    }
+
+    public List<CommitMessage> prepareCommitMessages(List<CommitMessage> 
messages) {
+        return messages;
+    }
+}
diff --git 
a/paimon-core/src/test/java/org/apache/paimon/append/cluster/HistoryPartitionClusterTest.java
 
b/paimon-core/src/test/java/org/apache/paimon/append/cluster/HistoryPartitionClusterTest.java
index 2d64663f9a..566d70e20d 100644
--- 
a/paimon-core/src/test/java/org/apache/paimon/append/cluster/HistoryPartitionClusterTest.java
+++ 
b/paimon-core/src/test/java/org/apache/paimon/append/cluster/HistoryPartitionClusterTest.java
@@ -79,7 +79,8 @@ public class HistoryPartitionClusterTest {
                                 table,
                                 PartitionPredicate.fromMultiple(
                                         RowType.of(DataTypes.INT()),
-                                        
Lists.newArrayList(BinaryRow.singleColumn("pt1"))))
+                                        
Lists.newArrayList(BinaryRow.singleColumn("pt1"))),
+                                table.snapshotManager().latestSnapshot())
                         .historyPartitionCluster();
         Map<BinaryRow, List<LevelSortedRun>> partitionLevels =
                 historyPartitionCluster.constructLevelsForHistoryPartitions();
@@ -92,14 +93,17 @@ public class HistoryPartitionClusterTest {
                                 table,
                                 PartitionPredicate.fromMultiple(
                                         RowType.of(DataTypes.INT()),
-                                        
Lists.newArrayList(BinaryRow.singleColumn("pt3"))))
+                                        
Lists.newArrayList(BinaryRow.singleColumn("pt3"))),
+                                table.snapshotManager().latestSnapshot())
                         .historyPartitionCluster();
         partitionLevels = 
historyPartitionCluster.constructLevelsForHistoryPartitions();
         assertThat(partitionLevels.size()).isEqualTo(1);
         
assertThat(partitionLevels.get(BinaryRow.singleColumn("pt1"))).isNotEmpty();
 
         // test not specify partition and disable history partition auto 
clustering
-        historyPartitionCluster = new 
IncrementalClusterManager(table).historyPartitionCluster();
+        historyPartitionCluster =
+                new IncrementalClusterManager(table, null, 
table.snapshotManager().latestSnapshot())
+                        .historyPartitionCluster();
         assertThat(historyPartitionCluster).isNull();
     }
 
diff --git 
a/paimon-core/src/test/java/org/apache/paimon/append/cluster/IncrementalClusterManagerTest.java
 
b/paimon-core/src/test/java/org/apache/paimon/append/cluster/IncrementalClusterManagerTest.java
index 464c3c7703..2997ec5a9e 100644
--- 
a/paimon-core/src/test/java/org/apache/paimon/append/cluster/IncrementalClusterManagerTest.java
+++ 
b/paimon-core/src/test/java/org/apache/paimon/append/cluster/IncrementalClusterManagerTest.java
@@ -64,19 +64,17 @@ public class IncrementalClusterManagerTest {
         options.put(CoreOptions.BUCKET.key(), "-1");
         options.put(CoreOptions.CLUSTERING_INCREMENTAL.key(), "false");
         FileStoreTable table = createTable(options, Collections.emptyList());
-        assertThatThrownBy(() -> new IncrementalClusterManager(table))
+        assertThatThrownBy(
+                        () ->
+                                new IncrementalClusterManager(
+                                        table, null, 
table.snapshotManager().latestSnapshot()))
                 .isInstanceOf(IllegalArgumentException.class)
                 .hasMessageContaining(
                         "Only support incremental clustering when 
'clustering.incremental' is true.");
     }
 
     @Test
-    public void testConstructPartitionLevels() throws Exception {
-        // Create a valid table for IncrementalClusterManager
-        Map<String, String> options = new HashMap<>();
-        FileStoreTable table = createTable(options, Collections.emptyList());
-        IncrementalClusterManager incrementalClusterManager = new 
IncrementalClusterManager(table);
-
+    public void testConstructPartitionLevels() {
         // Create test files with different levels
         List<DataFileMeta> partitionFiles = new ArrayList<>();
 
@@ -98,7 +96,7 @@ public class IncrementalClusterManagerTest {
 
         // Call the method under test
         List<LevelSortedRun> result =
-                
incrementalClusterManager.constructPartitionLevels(partitionFiles);
+                new 
IncrementalClusterRewriter().constructPartitionLevels(partitionFiles);
 
         // Verify the results
         assertThat(result).hasSize(4); // 2 level-0 runs + 1 level-1 run + 1 
level-2 run
@@ -152,7 +150,9 @@ public class IncrementalClusterManagerTest {
         // Test default mode is GLOBAL_SORT
         Map<String, String> options = new HashMap<>();
         FileStoreTable table = createTable(options, Collections.emptyList());
-        IncrementalClusterManager manager = new 
IncrementalClusterManager(table);
+        IncrementalClusterManager manager =
+                new IncrementalClusterManager(
+                        table, null, table.snapshotManager().latestSnapshot());
         assertThat(manager.clusteringIncrementalMode())
                 .isEqualTo(CoreOptions.ClusteringIncrementalMode.GLOBAL_SORT);
     }
@@ -163,7 +163,9 @@ public class IncrementalClusterManagerTest {
         Map<String, String> options = new HashMap<>();
         options.put(CoreOptions.CLUSTERING_INCREMENTAL_MODE.key(), 
"local-sort");
         FileStoreTable table = createTable(options, Collections.emptyList());
-        IncrementalClusterManager manager = new 
IncrementalClusterManager(table);
+        IncrementalClusterManager manager =
+                new IncrementalClusterManager(
+                        table, null, table.snapshotManager().latestSnapshot());
         assertThat(manager.clusteringIncrementalMode())
                 .isEqualTo(CoreOptions.ClusteringIncrementalMode.LOCAL_SORT);
     }
@@ -174,7 +176,9 @@ public class IncrementalClusterManagerTest {
         Map<String, String> options = new HashMap<>();
         options.put(CoreOptions.CLUSTERING_INCREMENTAL_MODE.key(), 
"global-sort");
         FileStoreTable table = createTable(options, Collections.emptyList());
-        IncrementalClusterManager manager = new 
IncrementalClusterManager(table);
+        IncrementalClusterManager manager =
+                new IncrementalClusterManager(
+                        table, null, table.snapshotManager().latestSnapshot());
         assertThat(manager.clusteringIncrementalMode())
                 .isEqualTo(CoreOptions.ClusteringIncrementalMode.GLOBAL_SORT);
     }
@@ -211,7 +215,8 @@ public class IncrementalClusterManagerTest {
                         table,
                         PartitionPredicate.fromMultiple(
                                 RowType.of(DataTypes.INT()),
-                                
Lists.newArrayList(BinaryRow.singleColumn("pt3"))));
+                                
Lists.newArrayList(BinaryRow.singleColumn("pt3"))),
+                        table.snapshotManager().latestSnapshot());
         Map<BinaryRow, CompactUnit> partitionLevels =
                 incrementalClusterManager.createCompactUnits(true);
         assertThat(partitionLevels.size()).isEqualTo(2);
@@ -219,21 +224,26 @@ public class IncrementalClusterManagerTest {
         
assertThat(partitionLevels.get(BinaryRow.singleColumn("pt3"))).isNotNull();
 
         // test don't specify partition and enable history partition auto 
clustering
-        incrementalClusterManager = new IncrementalClusterManager(table);
+        incrementalClusterManager =
+                new IncrementalClusterManager(
+                        table, null, table.snapshotManager().latestSnapshot());
         partitionLevels = incrementalClusterManager.createCompactUnits(true);
         assertThat(partitionLevels.size()).isEqualTo(4);
 
         // test specify partition and disable history partition auto clustering
         SchemaChange schemaChange =
                 
SchemaChange.removeOption(CoreOptions.CLUSTERING_HISTORY_PARTITION_IDLE_TIME.key());
+        FileStoreTable tableWithoutHistoryClustering =
+                table.copy(
+                        table.schemaManager()
+                                
.commitChanges(Collections.singletonList(schemaChange)));
         incrementalClusterManager =
                 new IncrementalClusterManager(
-                        table.copy(
-                                table.schemaManager()
-                                        
.commitChanges(Collections.singletonList(schemaChange))),
+                        tableWithoutHistoryClustering,
                         PartitionPredicate.fromMultiple(
                                 RowType.of(DataTypes.INT()),
-                                
Lists.newArrayList(BinaryRow.singleColumn("pt3"))));
+                                
Lists.newArrayList(BinaryRow.singleColumn("pt3"))),
+                        
tableWithoutHistoryClustering.snapshotManager().latestSnapshot());
         partitionLevels = incrementalClusterManager.createCompactUnits(true);
         assertThat(partitionLevels.size()).isEqualTo(1);
         
assertThat(partitionLevels.get(BinaryRow.singleColumn("pt3"))).isNotNull();
diff --git 
a/paimon-flink/paimon-flink-common/src/main/java/org/apache/paimon/flink/compact/IncrementalClusterCompact.java
 
b/paimon-flink/paimon-flink-common/src/main/java/org/apache/paimon/flink/compact/IncrementalClusterCompact.java
index a4f25486e1..0367adce8c 100644
--- 
a/paimon-flink/paimon-flink-common/src/main/java/org/apache/paimon/flink/compact/IncrementalClusterCompact.java
+++ 
b/paimon-flink/paimon-flink-common/src/main/java/org/apache/paimon/flink/compact/IncrementalClusterCompact.java
@@ -88,7 +88,9 @@ public class IncrementalClusterCompact {
             @Nullable Boolean fullCompaction) {
         this.env = env;
         this.table = table;
-        this.clusterManager = new IncrementalClusterManager(table, 
partitionPredicate);
+        this.clusterManager =
+                new IncrementalClusterManager(
+                        table, partitionPredicate, 
table.snapshotManager().latestSnapshot());
         Options options = new Options(table.options());
         this.partitionComputer = table.store().partitionComputer();
         this.commitUser = CoreOptions.createCommitUser(options);
diff --git 
a/paimon-spark/paimon-spark-common/src/main/java/org/apache/paimon/spark/procedure/CompactProcedure.java
 
b/paimon-spark/paimon-spark-common/src/main/java/org/apache/paimon/spark/procedure/CompactProcedure.java
index 218dc6d0a9..7acca09d85 100644
--- 
a/paimon-spark/paimon-spark-common/src/main/java/org/apache/paimon/spark/procedure/CompactProcedure.java
+++ 
b/paimon-spark/paimon-spark-common/src/main/java/org/apache/paimon/spark/procedure/CompactProcedure.java
@@ -844,7 +844,8 @@ public class CompactProcedure extends BaseProcedure {
             boolean fullCompaction,
             DataSourceV2Relation relation) {
         IncrementalClusterManager incrementalClusterManager =
-                new IncrementalClusterManager(table, partitionPredicate);
+                new IncrementalClusterManager(
+                        table, partitionPredicate, 
table.snapshotManager().latestSnapshot());
         Map<BinaryRow, CompactUnit> compactUnits =
                 incrementalClusterManager.createCompactUnits(fullCompaction);
         LOG.info(

Reply via email to