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 6b6e805008 [core] Fix bucket-first manifest sorting for postpone tables
6b6e805008 is described below

commit 6b6e8050088194512bffa72015a9562445a4f756
Author: JingsongLi <[email protected]>
AuthorDate: Mon Sep 14 10:15:19 2026 +0800

    [core] Fix bucket-first manifest sorting for postpone tables
---
 .../paimon/operation/ManifestCompactDryRun.java    |  3 +-
 .../paimon/operation/ManifestEntryRunMerge.java    |  6 +-
 .../paimon/operation/ManifestFileSorter.java       | 25 ++------
 .../paimon/manifest/ManifestFileMetaTest.java      | 73 ++++++++++++++++++----
 .../operation/ManifestEntryRunMergeTest.java       |  7 ++-
 .../operation/ManifestRewriteCleanupTest.java      |  2 +-
 6 files changed, 76 insertions(+), 40 deletions(-)

diff --git 
a/paimon-core/src/main/java/org/apache/paimon/operation/ManifestCompactDryRun.java
 
b/paimon-core/src/main/java/org/apache/paimon/operation/ManifestCompactDryRun.java
index 6f1e5f8e40..f80554ce6e 100644
--- 
a/paimon-core/src/main/java/org/apache/paimon/operation/ManifestCompactDryRun.java
+++ 
b/paimon-core/src/main/java/org/apache/paimon/operation/ManifestCompactDryRun.java
@@ -24,6 +24,7 @@ import org.apache.paimon.manifest.ManifestFile;
 import org.apache.paimon.manifest.ManifestFileMeta;
 import org.apache.paimon.manifest.ManifestList;
 import org.apache.paimon.options.MemorySize;
+import org.apache.paimon.table.BucketMode;
 import org.apache.paimon.table.FileStoreTable;
 import org.apache.paimon.types.RowType;
 
@@ -120,7 +121,7 @@ public class ManifestCompactDryRun {
                         manifests,
                         options.manifestSortPartitionField(),
                         partitionType,
-                        options.bucket() > 0);
+                        options.bucket() > 0 || options.bucket() == 
BucketMode.POSTPONE_BUCKET);
         ManifestFileSorter.ClassifyResult classifyResult =
                 ManifestFileSorter.classifyManifests(
                         manifests,
diff --git 
a/paimon-core/src/main/java/org/apache/paimon/operation/ManifestEntryRunMerge.java
 
b/paimon-core/src/main/java/org/apache/paimon/operation/ManifestEntryRunMerge.java
index 740b23f58d..aeb8e6ef62 100644
--- 
a/paimon-core/src/main/java/org/apache/paimon/operation/ManifestEntryRunMerge.java
+++ 
b/paimon-core/src/main/java/org/apache/paimon/operation/ManifestEntryRunMerge.java
@@ -71,7 +71,7 @@ final class ManifestEntryRunMerge {
     @Nullable
     static List<ManifestFileMeta> sortAndWriteFullEntries(
             List<ManifestFileMeta> section,
-            ManifestFileSorter.RowIdEntrySortKey sortKey,
+            ManifestFileSorter.RowIdSortKey sortKey,
             RowType partitionType,
             ManifestFile manifestFile,
             List<ManifestFileMeta> newFilesForAbort,
@@ -102,7 +102,7 @@ final class ManifestEntryRunMerge {
     @Nullable
     static Pair<List<ManifestFileMeta>, List<ManifestFileMeta>> 
sortAndWriteMinorEntries(
             List<ManifestFileMeta> section,
-            ManifestFileSorter.RowIdEntrySortKey sortKey,
+            ManifestFileSorter.RowIdSortKey sortKey,
             RowType partitionType,
             ManifestFile manifestFile,
             List<ManifestFileMeta> newFilesForAbort,
@@ -133,7 +133,7 @@ final class ManifestEntryRunMerge {
     @Nullable
     private static ManifestEntryRunMergePlan discoverRuns(
             List<ManifestFileMeta> section,
-            ManifestFileSorter.RowIdEntrySortKey sortKey,
+            ManifestFileSorter.RowIdSortKey sortKey,
             RowType partitionType,
             ManifestFile manifestFile,
             CollectedDeletes deletes,
diff --git 
a/paimon-core/src/main/java/org/apache/paimon/operation/ManifestFileSorter.java 
b/paimon-core/src/main/java/org/apache/paimon/operation/ManifestFileSorter.java
index 51f51dabfe..87fd4611e7 100644
--- 
a/paimon-core/src/main/java/org/apache/paimon/operation/ManifestFileSorter.java
+++ 
b/paimon-core/src/main/java/org/apache/paimon/operation/ManifestFileSorter.java
@@ -32,6 +32,7 @@ import org.apache.paimon.manifest.ManifestFile;
 import org.apache.paimon.manifest.ManifestFileMeta;
 import org.apache.paimon.manifest.ProjectedManifestEntry;
 import org.apache.paimon.partition.PartitionPredicate;
+import org.apache.paimon.table.BucketMode;
 import org.apache.paimon.types.DataType;
 import org.apache.paimon.types.DataTypes;
 import org.apache.paimon.types.RowType;
@@ -155,7 +156,7 @@ public class ManifestFileSorter {
             @Nullable IOManager ioManager)
             throws Exception {
         String sortPartitionField = options.manifestSortPartitionField();
-        boolean bucketed = options.bucket() > 0;
+        boolean bucketed = options.bucket() > 0 || options.bucket() == 
BucketMode.POSTPONE_BUCKET;
         boolean runMergeOptimizeEnabled = 
options.manifestMergeOptimizeEnabled();
         long suggestedMetaSize = options.manifestTargetSize().getBytes();
         int suggestedMinMetaCount = options.manifestMergeMinCount();
@@ -1110,7 +1111,7 @@ public class ManifestFileSorter {
             sorted =
                     ManifestEntryRunMerge.sortAndWriteFullEntries(
                             section,
-                            (RowIdEntrySortKey) ctx.sortKey,
+                            (RowIdSortKey) ctx.sortKey,
                             ctx.partitionType,
                             manifestFile,
                             sortNewFiles,
@@ -1152,7 +1153,7 @@ public class ManifestFileSorter {
             sorted =
                     ManifestEntryRunMerge.sortAndWriteMinorEntries(
                             section,
-                            (RowIdEntrySortKey) ctx.sortKey,
+                            (RowIdSortKey) ctx.sortKey,
                             ctx.partitionType,
                             manifestFile,
                             sortNewFiles,
@@ -1188,14 +1189,6 @@ public class ManifestFileSorter {
         return true;
     }
 
-    static ManifestSortKey createSortKey(
-            boolean dataEvolutionEnabled,
-            List<ManifestFileMeta> input,
-            String sortPartitionField,
-            RowType partitionType) {
-        return createSortKey(dataEvolutionEnabled, input, sortPartitionField, 
partitionType, false);
-    }
-
     static ManifestSortKey createSortKey(
             boolean dataEvolutionEnabled,
             List<ManifestFileMeta> input,
@@ -1289,11 +1282,6 @@ public class ManifestFileSorter {
         InternalRow binaryManifestRow(BinaryRow row);
     }
 
-    interface RowIdEntrySortKey extends ManifestSortKey {
-
-        int comparePartitions(BinaryRow left, BinaryRow right);
-    }
-
     private static class PartitionSortKey implements ManifestSortKey {
 
         private final RecordComparator fieldComparator;
@@ -1458,7 +1446,7 @@ public class ManifestFileSorter {
         }
     }
 
-    private static class RowIdSortKey implements RowIdEntrySortKey {
+    static class RowIdSortKey implements ManifestSortKey {
 
         @Nullable private final RecordComparator partitionComparator;
         private final InternalRow.FieldGetter[] partitionFieldGetters;
@@ -1545,8 +1533,7 @@ public class ManifestFileSorter {
             return row.getRow(sortFieldNum, 
ManifestEntry.MANIFEST_ROW_TYPE.getFieldCount());
         }
 
-        @Override
-        public int comparePartitions(BinaryRow left, BinaryRow right) {
+        int comparePartitions(BinaryRow left, BinaryRow right) {
             return partitionComparator == null ? 0 : 
partitionComparator.compare(left, right);
         }
 
diff --git 
a/paimon-core/src/test/java/org/apache/paimon/manifest/ManifestFileMetaTest.java
 
b/paimon-core/src/test/java/org/apache/paimon/manifest/ManifestFileMetaTest.java
index 898f238231..6d67eddfbd 100644
--- 
a/paimon-core/src/test/java/org/apache/paimon/manifest/ManifestFileMetaTest.java
+++ 
b/paimon-core/src/test/java/org/apache/paimon/manifest/ManifestFileMetaTest.java
@@ -19,6 +19,7 @@
 package org.apache.paimon.manifest;
 
 import org.apache.paimon.CoreOptions;
+import org.apache.paimon.Snapshot;
 import org.apache.paimon.data.BinaryRow;
 import org.apache.paimon.data.BinaryRowWriter;
 import org.apache.paimon.data.Timestamp;
@@ -30,11 +31,13 @@ import org.apache.paimon.fs.SeekableInputStream;
 import org.apache.paimon.fs.SeekableInputStreamWrapper;
 import org.apache.paimon.fs.local.LocalFileIO;
 import org.apache.paimon.io.DataFileMeta;
+import org.apache.paimon.operation.ManifestCompactDryRun;
 import org.apache.paimon.operation.ManifestFileMerger;
 import org.apache.paimon.options.Options;
 import org.apache.paimon.partition.PartitionPredicate;
 import org.apache.paimon.schema.FileSystemSchemaManager;
 import org.apache.paimon.stats.StatsTestUtils;
+import org.apache.paimon.table.FileStoreTable;
 import org.apache.paimon.types.IntType;
 import org.apache.paimon.types.RowType;
 import org.apache.paimon.utils.FailingFileIO;
@@ -48,6 +51,7 @@ import org.junit.jupiter.api.RepeatedTest;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.io.TempDir;
 import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.CsvSource;
 import org.junit.jupiter.params.provider.NullSource;
 import org.junit.jupiter.params.provider.ValueSource;
 
@@ -80,6 +84,9 @@ import java.util.stream.LongStream;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assumptions.assumeTrue;
+import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
 
 /** Tests for {@link ManifestFileMeta}. */
 public class ManifestFileMetaTest extends ManifestFileMetaTestBase {
@@ -1310,19 +1317,22 @@ public class ManifestFileMetaTest extends 
ManifestFileMetaTestBase {
                 .containsExactly(1, 3, 0, 2);
     }
 
-    @Test
-    public void testManifestSortUsesBucketAsPrimaryKeyForBucketedTable() {
+    @ParameterizedTest
+    @ValueSource(ints = {4, -2})
+    public void testManifestSortUsesBucketAsPrimaryKeyForBucketedTable(int 
bucket) {
+        int firstBucket = bucket == -2 ? -2 : 0;
         List<ManifestFileMeta> input =
                 Arrays.asList(
                         makeManifest(
-                                makeBucketEntry("a-b1-p1", 1, 1), 
makeBucketEntry("a-b0-p0", 0, 0)),
+                                makeBucketEntry("a-b1-p1", 1, 1),
+                                makeBucketEntry("a-first-p0", 0, firstBucket)),
                         makeManifest(
                                 makeBucketEntry("b-b1-p0", 0, 1),
-                                makeBucketEntry("b-b0-p1", 1, 0)));
+                                makeBucketEntry("b-first-p1", 1, 
firstBucket)));
 
         Options testOptions = new Options();
         testOptions.set(CoreOptions.MANIFEST_SORT_ENABLED, true);
-        testOptions.set(CoreOptions.BUCKET, 4);
+        testOptions.set(CoreOptions.BUCKET, bucket);
         testOptions.set(CoreOptions.MANIFEST_TARGET_FILE_SIZE.key(), "1G");
         testOptions.set(CoreOptions.MANIFEST_FULL_COMPACTION_FILE_SIZE.key(), 
"1B");
         List<ManifestFileMeta> merged =
@@ -1334,12 +1344,48 @@ public class ManifestFileMetaTest extends 
ManifestFileMetaTestBase {
 
         assertEquivalentEntries(input, merged);
         List<ManifestEntry> entries = readEntries(merged);
-        
assertThat(entries).extracting(ManifestEntry::bucket).containsExactly(0, 0, 1, 
1);
+        assertThat(entries)
+                .extracting(ManifestEntry::bucket)
+                .containsExactly(firstBucket, firstBucket, 1, 1);
         assertThat(entries)
                 .extracting(entry -> entry.partition().getInt(0))
                 .containsExactly(0, 1, 0, 1);
     }
 
+    @ParameterizedTest
+    @ValueSource(ints = {-1, 4, -2})
+    public void testManifestSortDryRunUsesBucketRangesForBucketedTable(int 
bucket) {
+        int firstBucket = bucket == -2 ? -2 : 0;
+        List<ManifestFileMeta> input =
+                Arrays.asList(
+                        makeManifest(
+                                makeBucketEntry("a-p0", 0, firstBucket),
+                                makeBucketEntry("a-p1", 1, firstBucket)),
+                        makeManifest(makeBucketEntry("b-p0", 0, 1), 
makeBucketEntry("b-p1", 1, 1)));
+
+        Options testOptions = new Options();
+        testOptions.set(CoreOptions.MANIFEST_SORT_ENABLED, true);
+        testOptions.set(CoreOptions.BUCKET, bucket);
+        testOptions.set(CoreOptions.MANIFEST_TARGET_FILE_SIZE.key(), "1B");
+        testOptions.set(CoreOptions.MANIFEST_FULL_COMPACTION_FILE_SIZE.key(), 
Long.MAX_VALUE + "B");
+
+        FileStoreTable table = mock(FileStoreTable.class, RETURNS_DEEP_STUBS);
+        Snapshot snapshot = mock(Snapshot.class);
+        when(table.options()).thenReturn(testOptions.toMap());
+        
when(table.store().snapshotManager().latestSnapshot()).thenReturn(snapshot);
+        
when(table.store().manifestListFactory().create().readDataManifests(snapshot))
+                .thenReturn(input);
+        
when(table.store().manifestFileFactory().create()).thenReturn(manifestFile);
+        
when(table.schema().logicalPartitionType()).thenReturn(getPartitionType());
+
+        // Overlapping partition ranges form one run only when buckets take 
precedence.
+        assertThat(ManifestCompactDryRun.execute(table))
+                .endsWith(
+                        bucket == -1
+                                ? "Manifest sort level files: L0=0, L1=0, 
L2=0, L3=1, L4=1."
+                                : "Manifest sort level files: L0=0, L1=0, 
L2=0, L3=0, L4=2.");
+    }
+
     @Test
     public void testManifestSortMinorCompactionRespectsMergeMinCount() {
         List<ManifestFileMeta> input = new ArrayList<>();
@@ -1447,30 +1493,31 @@ public class ManifestFileMetaTest extends 
ManifestFileMetaTestBase {
     }
 
     @ParameterizedTest
-    @ValueSource(booleans = {false, true})
-    public void testManifestSortWithSpillableExternalSortBuffer(boolean 
bucketed) {
+    @CsvSource({"-1, false", "4, true", "-2, true"})
+    public void testManifestSortWithSpillableExternalSortBuffer(int bucket, 
boolean bucketed) {
         List<ManifestFileMeta> input = new ArrayList<>();
         for (int manifest = 0; manifest < 4; manifest++) {
             List<ManifestEntry> entries = new ArrayList<>();
             for (int i = 0; i < 80; i++) {
                 int partition = manifest % 2 == 0 ? 79 - i : i;
-                int bucket = Math.floorMod(manifest * 31 + i * 17, 4);
+                int entryBucket = Math.floorMod(manifest * 31 + i * 17, 4);
+                if (bucket == -2 && entryBucket == 3) {
+                    entryBucket = -2;
+                }
                 entries.add(
                         makeBucketEntry(
                                 String.format(
                                         
"spill-manifest-%02d-entry-%03d-payload-padding-%040d",
                                         manifest, i, i),
                                 partition,
-                                bucket));
+                                entryBucket));
             }
             input.add(makeManifest(entries.toArray(new ManifestEntry[0])));
         }
 
         Options testOptions = new Options();
         testOptions.set("manifest-sort.enabled", "true");
-        if (bucketed) {
-            testOptions.set(CoreOptions.BUCKET, 4);
-        }
+        testOptions.set(CoreOptions.BUCKET, bucket);
         testOptions.set("manifest.full-compaction-threshold-size", "1B");
         testOptions.set("page-size", "1kb");
         testOptions.set("sort-spill-buffer-size", "4kb");
diff --git 
a/paimon-core/src/test/java/org/apache/paimon/operation/ManifestEntryRunMergeTest.java
 
b/paimon-core/src/test/java/org/apache/paimon/operation/ManifestEntryRunMergeTest.java
index 1142f2fa16..dbd8ef66a1 100644
--- 
a/paimon-core/src/test/java/org/apache/paimon/operation/ManifestEntryRunMergeTest.java
+++ 
b/paimon-core/src/test/java/org/apache/paimon/operation/ManifestEntryRunMergeTest.java
@@ -76,9 +76,10 @@ class ManifestEntryRunMergeTest extends 
ManifestFileMetaTestBase {
         List<ManifestFileMeta> manifests = new ArrayList<>();
         manifests.add(makeManifest(firstManifest.toArray(new 
ManifestEntry[0])));
         manifests.add(makeManifest(secondManifest.toArray(new 
ManifestEntry[0])));
-        ManifestFileSorter.RowIdEntrySortKey sortKey =
-                (ManifestFileSorter.RowIdEntrySortKey)
-                        ManifestFileSorter.createSortKey(true, manifests, 
null, partitionType);
+        ManifestFileSorter.RowIdSortKey sortKey =
+                (ManifestFileSorter.RowIdSortKey)
+                        ManifestFileSorter.createSortKey(
+                                true, manifests, null, partitionType, false);
 
         CollectedDeletes deletes = new CollectedDeletes(true);
         List<ManifestFileMeta> output;
diff --git 
a/paimon-core/src/test/java/org/apache/paimon/operation/ManifestRewriteCleanupTest.java
 
b/paimon-core/src/test/java/org/apache/paimon/operation/ManifestRewriteCleanupTest.java
index 2049011bc8..8247d23262 100644
--- 
a/paimon-core/src/test/java/org/apache/paimon/operation/ManifestRewriteCleanupTest.java
+++ 
b/paimon-core/src/test/java/org/apache/paimon/operation/ManifestRewriteCleanupTest.java
@@ -598,7 +598,7 @@ class ManifestRewriteCleanupTest extends 
ManifestFileMetaTestBase {
     private ManifestFileSorter.ManifestSortKey failingSortKey(
             List<ManifestFileMeta> input, int successfulRowsBeforeFailure, 
AssertionError failure) {
         return new FailingManifestSortKey(
-                ManifestFileSorter.createSortKey(true, input, null, 
PARTITION_TYPE),
+                ManifestFileSorter.createSortKey(true, input, null, 
PARTITION_TYPE, false),
                 successfulRowsBeforeFailure,
                 failure);
     }

Reply via email to