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 a0226d338c [core] Support row ID reassignment across concurrent 
compaction (#10780)
a0226d338c is described below

commit a0226d338cfa936ad65e45ed0687da16fbd452ba
Author: YeJunHao <[email protected]>
AuthorDate: Mon Oct 26 16:40:06 2026 +0800

    [core] Support row ID reassignment across concurrent compaction (#10780)
---
 .../DataEvolutionRowIdReassigner.java              |  25 +-
 .../DataEvolutionRowIdReassignerTest.java          | 726 +--------------------
 .../table/DataEvolutionDeletionVectorTest.java     |  55 +-
 3 files changed, 91 insertions(+), 715 deletions(-)

diff --git 
a/paimon-core/src/main/java/org/apache/paimon/append/dataevolution/DataEvolutionRowIdReassigner.java
 
b/paimon-core/src/main/java/org/apache/paimon/append/dataevolution/DataEvolutionRowIdReassigner.java
index e357decb6b..6a6d87dc46 100644
--- 
a/paimon-core/src/main/java/org/apache/paimon/append/dataevolution/DataEvolutionRowIdReassigner.java
+++ 
b/paimon-core/src/main/java/org/apache/paimon/append/dataevolution/DataEvolutionRowIdReassigner.java
@@ -487,8 +487,7 @@ public class DataEvolutionRowIdReassigner {
                         e);
             }
 
-            if (snapshot.commitKind() == Snapshot.CommitKind.COMPACT
-                    || snapshot.commitKind() == Snapshot.CommitKind.OVERWRITE) 
{
+            if (snapshot.commitKind() == Snapshot.CommitKind.OVERWRITE) {
                 throw new RuntimeException(
                         String.format(
                                 "Abort row-id reassignment because %s snapshot 
%s was committed after snapshot %s.",
@@ -498,14 +497,15 @@ public class DataEvolutionRowIdReassigner {
                 continue;
             }
             checkState(
-                    snapshot.commitKind() == Snapshot.CommitKind.APPEND,
+                    snapshot.commitKind() == Snapshot.CommitKind.APPEND
+                            || snapshot.commitKind() == 
Snapshot.CommitKind.COMPACT,
                     "Unsupported snapshot kind %s while advancing row-id 
assignment.",
                     snapshot.commitKind());
 
             for (ManifestFileMeta manifestMeta : 
manifestList.readDeltaManifests(snapshot)) {
                 boolean needsReassign =
-                        appendedManifestNeedsReassign(
-                                assignmentPlan, manifestFile, manifestMeta, 
snapshot.id());
+                        deltaManifestNeedsReassign(
+                                assignmentPlan, manifestFile, manifestMeta, 
snapshot);
                 newManifestNeedsReassign.put(manifestMeta.fileName(), 
needsReassign);
                 if (needsReassign) {
                     manifestMetasToRewrite.put(manifestMeta.fileName(), 
manifestMeta);
@@ -562,11 +562,11 @@ public class DataEvolutionRowIdReassigner {
         }
     }
 
-    private boolean appendedManifestNeedsReassign(
+    private boolean deltaManifestNeedsReassign(
             AssignmentPlan assignmentPlan,
             ManifestFile manifestFile,
             ManifestFileMeta manifestMeta,
-            long appendSnapshotId) {
+            Snapshot snapshot) {
         boolean needsReassign = false;
         try (CloseableIterator<ProjectedManifestEntry> entries =
                 manifestFile.scan(
@@ -577,11 +577,11 @@ public class DataEvolutionRowIdReassigner {
                     continue;
                 }
                 checkState(
-                        entry.isAdd(),
+                        snapshot.commitKind() != Snapshot.CommitKind.APPEND || 
entry.isAdd(),
                         "APPEND snapshot %s contains a non-ADD entry in 
manifest %s.",
-                        appendSnapshotId,
+                        snapshot.id(),
                         manifestMeta.fileName());
-                if (appendedEntryNeedsReassign(assignmentPlan, entry)) {
+                if (entryNeedsReassign(assignmentPlan, entry)) {
                     needsReassign = true;
                 }
             }
@@ -594,8 +594,7 @@ public class DataEvolutionRowIdReassigner {
         }
     }
 
-    private boolean appendedEntryNeedsReassign(
-            AssignmentPlan assignmentPlan, ManifestEntry appendedEntry) {
+    private boolean entryNeedsReassign(AssignmentPlan assignmentPlan, 
ManifestEntry appendedEntry) {
         RowRangeMappingIndex mapping =
                 
assignmentPlan.relativeRowIdMappings.mappings.get(appendedEntry.partition());
         if (mapping == null) {
@@ -609,7 +608,7 @@ public class DataEvolutionRowIdReassigner {
 
         checkState(
                 !mapping.overlaps(appendedRange),
-                "Cannot advance row-id assignment because appended row-id 
range %s partially overlaps planned ranges in partition %s.",
+                "Cannot advance row-id assignment because row-id range %s 
partially overlaps planned ranges in partition %s.",
                 appendedRange,
                 appendedEntry.partition());
         return false;
diff --git 
a/paimon-core/src/test/java/org/apache/paimon/append/dataevolution/DataEvolutionRowIdReassignerTest.java
 
b/paimon-core/src/test/java/org/apache/paimon/append/dataevolution/DataEvolutionRowIdReassignerTest.java
index abf14e67b1..b0ad384968 100644
--- 
a/paimon-core/src/test/java/org/apache/paimon/append/dataevolution/DataEvolutionRowIdReassignerTest.java
+++ 
b/paimon-core/src/test/java/org/apache/paimon/append/dataevolution/DataEvolutionRowIdReassignerTest.java
@@ -94,8 +94,6 @@ import javax.annotation.Nullable;
 
 import java.io.IOException;
 import java.io.OutputStream;
-import java.lang.reflect.Field;
-import java.lang.reflect.Method;
 import java.nio.ByteBuffer;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -106,7 +104,6 @@ import java.util.LinkedHashMap;
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
-import java.util.Optional;
 import java.util.Random;
 import java.util.Set;
 import java.util.UUID;
@@ -247,408 +244,6 @@ public class DataEvolutionRowIdReassignerTest extends 
TableTestBase {
         return schemaBuilder.build();
     }
 
-    @Test
-    public void testCompactAndLegacyPlansMatchOnSameHistoricalSnapshot() 
throws Exception {
-        FileStoreTable table = createTableWithInterleavedPartitions();
-        FileEntry.Identifier historicalFile = compactOneFile(table, "pt=a/");
-        assertAddDeleteEntriesConsistent(table, historicalFile);
-
-        Snapshot snapshot = table.snapshotManager().latestSnapshot();
-        ManifestList manifestList = 
table.store().manifestListFactory().create();
-        List<ManifestFileMeta> manifestMetas = 
manifestList.readDataManifests(snapshot);
-        List<String> beforeManifestFiles = dataManifestFileNames(table);
-        int[] historicalManifestOrdinals =
-                manifestOrdinalsContaining(table, manifestMetas, 
historicalFile);
-        assertThat(historicalManifestOrdinals).hasSize(2);
-
-        Optional<AssignmentPlanView> compactPlan = compactPlanView(table, 
manifestMetas, null);
-        Optional<AssignmentPlanView> legacyPlan = legacyPlanView(table, 
manifestMetas, null);
-        assertPlanViewsEqual(compactPlan, legacyPlan);
-        assertThat(compactPlan).isPresent();
-        
assertThat(compactPlan.get().manifestOrdinals).contains(historicalManifestOrdinals);
-
-        
assertThat(table.snapshotManager().latestSnapshot().id()).isEqualTo(snapshot.id());
-        
assertThat(dataManifestFileNames(table)).containsExactlyElementsOf(beforeManifestFiles);
-    }
-
-    @Test
-    public void testCompactAndLegacyEmptyPlansMatch() throws Exception {
-        createTableDefault();
-        FileStoreTable table = getTableDefault();
-        writeRows(table, "a", 0, 1);
-        writeRows(table, "a", 2);
-        writeRows(table, "b", 3, 4);
-
-        Snapshot snapshot = table.snapshotManager().latestSnapshot();
-        ManifestList manifestList = 
table.store().manifestListFactory().create();
-        List<ManifestFileMeta> manifestMetas = 
manifestList.readDataManifests(snapshot);
-
-        Optional<AssignmentPlanView> compactPlan = compactPlanView(table, 
manifestMetas, null);
-        Optional<AssignmentPlanView> legacyPlan = legacyPlanView(table, 
manifestMetas, null);
-        assertPlanViewsEqual(compactPlan, legacyPlan);
-        assertThat(compactPlan).isEmpty();
-        
assertThat(table.snapshotManager().latestSnapshot().id()).isEqualTo(snapshot.id());
-    }
-
-    @Test
-    public void testCompactAndLegacyPlansMatchWithPartitionPredicate() throws 
Exception {
-        FileStoreTable table = createTableWithInterleavedPartitions();
-        Snapshot snapshot = table.snapshotManager().latestSnapshot();
-        ManifestList manifestList = 
table.store().manifestListFactory().create();
-        List<ManifestFileMeta> manifestMetas = 
manifestList.readDataManifests(snapshot);
-        PartitionPredicate onlyA = partitionPredicate(table, "a");
-
-        Optional<AssignmentPlanView> compactPlan = compactPlanView(table, 
manifestMetas, onlyA);
-        Optional<AssignmentPlanView> legacyPlan = legacyPlanView(table, 
manifestMetas, onlyA);
-        assertPlanViewsEqual(compactPlan, legacyPlan);
-        assertThat(compactPlan).isPresent();
-        assertThat(compactPlan.get().partitionMappings).hasSize(1);
-    }
-
-    @Test
-    public void testCompactAndLegacyPlansMatchWithoutPartitionStats() throws 
Exception {
-        FileStoreTable table = createTableWithInterleavedPartitions();
-        Snapshot snapshot = table.snapshotManager().latestSnapshot();
-        List<ManifestFileMeta> manifestMetas =
-                
table.store().manifestListFactory().create().readDataManifests(snapshot);
-        List<ManifestFileMeta> metasWithoutPartitionStats = new 
ArrayList<>(manifestMetas.size());
-        for (ManifestFileMeta manifestMeta : manifestMetas) {
-            
metasWithoutPartitionStats.add(withoutPartitionStats(manifestMeta));
-        }
-
-        Optional<AssignmentPlanView> compactPlan =
-                compactPlanView(table, metasWithoutPartitionStats, null);
-        Optional<AssignmentPlanView> legacyPlan =
-                legacyPlanView(table, metasWithoutPartitionStats, null);
-        assertPlanViewsEqual(compactPlan, legacyPlan);
-        assertThat(compactPlan).isPresent();
-    }
-
-    @Test
-    public void testCompactAndLegacyPlansMatchForSpecialFileRanges() throws 
Exception {
-        createTableDefault();
-        FileStoreTable table = getTableDefault();
-        InternalRowSerializer partitionSerializer =
-                new 
InternalRowSerializer(table.schema().logicalPartitionType());
-        BinaryRow partitionA =
-                
partitionSerializer.toBinaryRow(GenericRow.of(BinaryString.fromString("a"))).copy();
-        BinaryRow partitionB =
-                
partitionSerializer.toBinaryRow(GenericRow.of(BinaryString.fromString("b"))).copy();
-        BinaryRow partitionC =
-                
partitionSerializer.toBinaryRow(GenericRow.of(BinaryString.fromString("c"))).copy();
-
-        List<ManifestEntry> entries =
-                Arrays.asList(
-                        planningManifestEntry(partitionA, 
"ordinary-a-0.parquet", 10L, 10L),
-                        planningManifestEntry(partitionA, "ordinary-a-0.blob", 
12L, 4L),
-                        planningManifestEntry(partitionA, 
"ordinary-a-0.vector.bin", 16L, 2L),
-                        planningManifestEntry(partitionA, 
"ordinary-a-1.parquet", 30L, 10L),
-                        planningManifestEntry(partitionB, 
"ordinary-b-0.parquet", 0L, 5L),
-                        planningManifestEntry(partitionB, 
"ordinary-b-1.parquet", 5L, 5L),
-                        planningManifestEntry(partitionC, "special-c-0.blob", 
50L, 6L),
-                        planningManifestEntry(partitionC, 
"special-c-0.vector.bin", 54L, 7L),
-                        planningManifestEntry(partitionC, "special-c-1.blob", 
80L, 3L),
-                        planningManifestEntry(partitionC, 
"special-c-1.vector.bin", 82L, 4L));
-        List<ManifestFileMeta> manifestMetas =
-                table.store().manifestFileFactory().create().write(entries);
-
-        Optional<AssignmentPlanView> compactPlan = compactPlanView(table, 
manifestMetas, null);
-        Optional<AssignmentPlanView> legacyPlan = legacyPlanView(table, 
manifestMetas, null);
-        assertPlanViewsEqual(compactPlan, legacyPlan);
-        assertThat(compactPlan).isPresent();
-        assertThat(compactPlan.get().partitionMappings).hasSize(2);
-        PartitionMappingView ordinaryMapping = 
compactPlan.get().partitionMappings.get(0);
-        assertThat(ordinaryMapping.oldStarts).containsExactly(10L, 30L);
-        assertThat(ordinaryMapping.oldEnds).containsExactly(19L, 39L);
-        assertThat(ordinaryMapping.newStarts).containsExactly(0L, 10L);
-        PartitionMappingView allSpecialMapping = 
compactPlan.get().partitionMappings.get(1);
-        assertThat(allSpecialMapping.oldStarts).containsExactly(50L, 80L);
-        assertThat(allSpecialMapping.oldEnds).containsExactly(60L, 85L);
-        assertThat(allSpecialMapping.newStarts).containsExactly(20L, 31L);
-        assertThat(compactPlan.get().totalOffset).isEqualTo(37L);
-    }
-
-    @Test
-    public void testCompactAndLegacyPlansMatchForRawNaNPartitionPayloads() 
throws Exception {
-        Schema.Builder schemaBuilder = Schema.newBuilder();
-        schemaBuilder.column("float_pt", DataTypes.FLOAT());
-        schemaBuilder.column("double_pt", DataTypes.DOUBLE());
-        schemaBuilder.column("id", DataTypes.INT());
-        schemaBuilder.partitionKeys("float_pt", "double_pt");
-        schemaBuilder.option(CoreOptions.ROW_TRACKING_ENABLED.key(), "true");
-        schemaBuilder.option(CoreOptions.DATA_EVOLUTION_ENABLED.key(), "true");
-        catalog.createTable(identifier(), schemaBuilder.build(), true);
-        FileStoreTable table = getTableDefault();
-
-        InternalRowSerializer partitionSerializer =
-                new 
InternalRowSerializer(table.schema().logicalPartitionType());
-        float floatNaN1 = Float.intBitsToFloat(0x7fc00001);
-        float floatNaN2 = Float.intBitsToFloat(0x7fc00002);
-        double doubleNaN1 = Double.longBitsToDouble(0x7ff8000000000001L);
-        double doubleNaN2 = Double.longBitsToDouble(0x7ff8000000000002L);
-        BinaryRow doubleScanFirst =
-                partitionSerializer.toBinaryRow(GenericRow.of(0.0f, 
doubleNaN1)).copy();
-        BinaryRow doubleLegacyFirst =
-                partitionSerializer.toBinaryRow(GenericRow.of(0.0f, 
doubleNaN2)).copy();
-        BinaryRow floatScanFirst =
-                partitionSerializer.toBinaryRow(GenericRow.of(floatNaN1, 
0.0d)).copy();
-        BinaryRow floatLegacyFirst =
-                partitionSerializer.toBinaryRow(GenericRow.of(floatNaN2, 
0.0d)).copy();
-
-        
assertThat(doubleScanFirst.toBytes()).isNotEqualTo(doubleLegacyFirst.toBytes());
-        
assertThat(floatScanFirst.toBytes()).isNotEqualTo(floatLegacyFirst.toBytes());
-
-        List<ManifestEntry> entries =
-                Arrays.asList(
-                        planningManifestEntry(doubleScanFirst, 
"double-scan-100.parquet", 100L, 1L),
-                        planningManifestEntry(
-                                doubleLegacyFirst, "double-legacy-10.parquet", 
10L, 1L),
-                        planningManifestEntry(doubleScanFirst, 
"double-scan-102.parquet", 102L, 1L),
-                        planningManifestEntry(
-                                doubleLegacyFirst, "double-legacy-12.parquet", 
12L, 1L),
-                        planningManifestEntry(floatScanFirst, 
"float-scan-300.parquet", 300L, 1L),
-                        planningManifestEntry(
-                                floatLegacyFirst, "float-legacy-200.parquet", 
200L, 1L),
-                        planningManifestEntry(floatScanFirst, 
"float-scan-302.parquet", 302L, 1L),
-                        planningManifestEntry(
-                                floatLegacyFirst, "float-legacy-202.parquet", 
202L, 1L));
-        List<ManifestFileMeta> manifestMetas =
-                table.store().manifestFileFactory().create().write(entries);
-        assertThat(manifestMetas).hasSize(1);
-
-        Optional<AssignmentPlanView> compactPlan = compactPlanView(table, 
manifestMetas, null);
-        Optional<AssignmentPlanView> legacyPlan = legacyPlanView(table, 
manifestMetas, null);
-        assertPlanViewsEqual(compactPlan, legacyPlan);
-
-        assertThat(compactPlan).isPresent();
-        AssignmentPlanView plan = compactPlan.get();
-        assertThat(plan.manifestOrdinals).containsExactly(0);
-        assertThat(plan.partitionMappings).hasSize(4);
-
-        PartitionMappingView doubleLegacyMapping = 
plan.partitionMappings.get(0);
-        
assertThat(doubleLegacyMapping.partitionBytes).containsExactly(doubleLegacyFirst.toBytes());
-        assertThat(doubleLegacyMapping.oldStarts).containsExactly(10L, 12L);
-        assertThat(doubleLegacyMapping.oldEnds).containsExactly(10L, 12L);
-        assertThat(doubleLegacyMapping.newStarts).containsExactly(0L, 1L);
-
-        PartitionMappingView doubleScanMapping = plan.partitionMappings.get(1);
-        
assertThat(doubleScanMapping.partitionBytes).containsExactly(doubleScanFirst.toBytes());
-        assertThat(doubleScanMapping.oldStarts).containsExactly(100L, 102L);
-        assertThat(doubleScanMapping.oldEnds).containsExactly(100L, 102L);
-        assertThat(doubleScanMapping.newStarts).containsExactly(2L, 3L);
-
-        PartitionMappingView floatLegacyMapping = 
plan.partitionMappings.get(2);
-        
assertThat(floatLegacyMapping.partitionBytes).containsExactly(floatLegacyFirst.toBytes());
-        assertThat(floatLegacyMapping.oldStarts).containsExactly(200L, 202L);
-        assertThat(floatLegacyMapping.oldEnds).containsExactly(200L, 202L);
-        assertThat(floatLegacyMapping.newStarts).containsExactly(4L, 5L);
-
-        PartitionMappingView floatScanMapping = plan.partitionMappings.get(3);
-        
assertThat(floatScanMapping.partitionBytes).containsExactly(floatScanFirst.toBytes());
-        assertThat(floatScanMapping.oldStarts).containsExactly(300L, 302L);
-        assertThat(floatScanMapping.oldEnds).containsExactly(300L, 302L);
-        assertThat(floatScanMapping.newStarts).containsExactly(6L, 7L);
-        assertThat(plan.totalOffset).isEqualTo(8L);
-    }
-
-    @Test
-    public void testDeleteEntryAtEndDoesNotAffectEqualPartitionOrdering() 
throws Exception {
-        Schema.Builder schemaBuilder = Schema.newBuilder();
-        schemaBuilder.column("float_pt", DataTypes.FLOAT());
-        schemaBuilder.column("id", DataTypes.INT());
-        schemaBuilder.partitionKeys("float_pt");
-        schemaBuilder.option(CoreOptions.ROW_TRACKING_ENABLED.key(), "true");
-        schemaBuilder.option(CoreOptions.DATA_EVOLUTION_ENABLED.key(), "true");
-        catalog.createTable(identifier(), schemaBuilder.build(), true);
-        FileStoreTable table = getTableDefault();
-
-        InternalRowSerializer partitionSerializer =
-                new 
InternalRowSerializer(table.schema().logicalPartitionType());
-        BinaryRow laterPartition =
-                partitionSerializer
-                        
.toBinaryRow(GenericRow.of(Float.intBitsToFloat(0x7fc00001)))
-                        .copy();
-        BinaryRow earlierPartition =
-                partitionSerializer
-                        
.toBinaryRow(GenericRow.of(Float.intBitsToFloat(0x7fc00002)))
-                        .copy();
-        
assertThat(laterPartition.toBytes()).isNotEqualTo(earlierPartition.toBytes());
-
-        DataFileMeta deletedFile =
-                planningDataFile("deleted-0.parquet", 0L, 1, 
Collections.emptyList(), null, null);
-        List<ManifestEntry> entries =
-                Arrays.asList(
-                        ManifestEntry.create(FileKind.ADD, laterPartition, 0, 
1, deletedFile),
-                        planningManifestEntry(earlierPartition, 
"earlier-live-10.parquet", 10L, 1L),
-                        planningManifestEntry(earlierPartition, 
"earlier-live-12.parquet", 12L, 1L),
-                        planningManifestEntry(laterPartition, 
"later-live-100.parquet", 100L, 1L),
-                        planningManifestEntry(laterPartition, 
"later-live-102.parquet", 102L, 1L),
-                        // Keep DELETE after its ADD, matching manifest 
compaction ordering.
-                        ManifestEntry.create(FileKind.DELETE, laterPartition, 
0, 1, deletedFile));
-        ManifestFile manifestFile = 
table.store().manifestFileFactory().create();
-        List<ManifestFileMeta> manifestMetas = manifestFile.write(entries);
-        assertThat(manifestMetas).hasSize(1);
-        assertThat(
-                        manifestFile.read(
-                                manifestMetas.get(0).fileName(), 
manifestMetas.get(0).fileSize()))
-                .extracting(ManifestEntry::kind)
-                .containsExactly(
-                        FileKind.ADD,
-                        FileKind.ADD,
-                        FileKind.ADD,
-                        FileKind.ADD,
-                        FileKind.ADD,
-                        FileKind.DELETE);
-
-        Optional<AssignmentPlanView> compactPlan = compactPlanView(table, 
manifestMetas, null);
-        Optional<AssignmentPlanView> legacyPlan = legacyPlanView(table, 
manifestMetas, null);
-        assertPlanViewsEqual(compactPlan, legacyPlan);
-
-        assertThat(compactPlan).isPresent();
-        AssignmentPlanView plan = compactPlan.get();
-        assertThat(plan.partitionMappings).hasSize(2);
-        PartitionMappingView earlierMapping = plan.partitionMappings.get(0);
-        
assertThat(earlierMapping.partitionBytes).containsExactly(earlierPartition.toBytes());
-        assertThat(earlierMapping.oldStarts).containsExactly(10L, 12L);
-        PartitionMappingView laterMapping = plan.partitionMappings.get(1);
-        
assertThat(laterMapping.partitionBytes).containsExactly(laterPartition.toBytes());
-        assertThat(laterMapping.oldStarts).containsExactly(100L, 102L);
-        assertThat(plan.totalOffset).isEqualTo(4L);
-    }
-
-    @Test
-    public void testCompactAndLegacyPlansMatchForComplexDeleteIdentifier() 
throws Exception {
-        createTableDefault();
-        FileStoreTable table = getTableDefault();
-        BinaryRow partition =
-                new 
InternalRowSerializer(table.schema().logicalPartitionType())
-                        
.toBinaryRow(GenericRow.of(BinaryString.fromString("a")))
-                        .copy();
-        DataFileMeta deletedFile =
-                DataFileMeta.forAppend(
-                                "deleted.parquet",
-                                1L,
-                                1L,
-                                SimpleStats.EMPTY_STATS,
-                                7L,
-                                9L,
-                                0L,
-                                Arrays.asList("extra-0", "extra-1"),
-                                new byte[] {1, 2, 3, 4},
-                                FileSource.APPEND,
-                                null,
-                                "/external/deleted.parquet",
-                                1L,
-                                null)
-                        .upgrade(3);
-        ManifestFile manifestFile = 
table.store().manifestFileFactory().create();
-        List<ManifestFileMeta> manifestMetas = new ArrayList<>();
-        manifestMetas.addAll(
-                manifestFile.write(
-                        Arrays.asList(
-                                planningManifestEntry(partition, 
"current-0.parquet", 0L, 1L),
-                                ManifestEntry.create(FileKind.ADD, partition, 
7, 8, deletedFile),
-                                planningManifestEntry(partition, 
"current-2.parquet", 2L, 1L))));
-        manifestMetas.addAll(
-                manifestFile.write(
-                        Collections.singletonList(
-                                ManifestEntry.create(
-                                        FileKind.DELETE, partition, 7, 8, 
deletedFile))));
-
-        Optional<AssignmentPlanView> compactPlan = compactPlanView(table, 
manifestMetas, null);
-        Optional<AssignmentPlanView> legacyPlan = legacyPlanView(table, 
manifestMetas, null);
-        assertPlanViewsEqual(compactPlan, legacyPlan);
-        assertThat(compactPlan).isPresent();
-        assertThat(compactPlan.get().partitionMappings).hasSize(1);
-        PartitionMappingView mapping = 
compactPlan.get().partitionMappings.get(0);
-        assertThat(mapping.oldStarts).containsExactly(0L, 2L);
-        assertThat(mapping.oldEnds).containsExactly(0L, 2L);
-        assertThat(mapping.newStarts).containsExactly(0L, 1L);
-    }
-
-    @Test
-    public void testCompactAndLegacyPlansMatchForDeleteIdentifierBoundaries() 
throws Exception {
-        createTableDefault();
-        FileStoreTable table = getTableDefault();
-        InternalRowSerializer partitionSerializer =
-                new 
InternalRowSerializer(table.schema().logicalPartitionType());
-        BinaryRow partitionA =
-                
partitionSerializer.toBinaryRow(GenericRow.of(BinaryString.fromString("a"))).copy();
-        BinaryRow partitionB =
-                
partitionSerializer.toBinaryRow(GenericRow.of(BinaryString.fromString("b"))).copy();
-
-        List<String> extraFiles = Arrays.asList("extra-0", "extra-1");
-        byte[] embeddedIndex = {1, 2, 3, 4};
-        String externalPath = "/external/identity.parquet";
-        DataFileMeta identityFile =
-                planningDataFile(
-                        "identity.parquet", 2L, 3, extraFiles, embeddedIndex, 
externalPath);
-        DataFileMeta totalBucketsFile =
-                planningDataFile(
-                        "total-buckets.parquet", 6L, 3, extraFiles, 
embeddedIndex, externalPath);
-
-        List<ManifestEntry> entries = new ArrayList<>();
-        entries.add(planningManifestEntry(partitionA, "anchor-0.parquet", 0L, 
1L));
-        entries.add(ManifestEntry.create(FileKind.ADD, partitionA, 7, 8, 
identityFile));
-        entries.add(planningManifestEntry(partitionA, "anchor-4.parquet", 4L, 
1L));
-        entries.add(ManifestEntry.create(FileKind.ADD, partitionA, 9, 16, 
totalBucketsFile));
-
-        // Each DELETE differs from identityFile in exactly one Identifier 
field.
-        entries.add(ManifestEntry.create(FileKind.DELETE, partitionB, 7, 8, 
identityFile));
-        entries.add(ManifestEntry.create(FileKind.DELETE, partitionA, 6, 8, 
identityFile));
-        List<DataFileMeta> fileFieldNearMisses =
-                Arrays.asList(
-                        identityFile.upgrade(4),
-                        planningDataFile(
-                                "different-name.parquet",
-                                2L,
-                                3,
-                                extraFiles,
-                                embeddedIndex,
-                                externalPath),
-                        planningDataFile(
-                                "identity.parquet",
-                                2L,
-                                3,
-                                Arrays.asList("extra-1", "extra-0"),
-                                embeddedIndex,
-                                externalPath),
-                        planningDataFile(
-                                "identity.parquet",
-                                2L,
-                                3,
-                                extraFiles,
-                                new byte[] {1, 2, 3, 5},
-                                externalPath),
-                        planningDataFile(
-                                "identity.parquet",
-                                2L,
-                                3,
-                                extraFiles,
-                                embeddedIndex,
-                                "/external/different.parquet"));
-        for (DataFileMeta nearMiss : fileFieldNearMisses) {
-            entries.add(ManifestEntry.create(FileKind.DELETE, partitionA, 7, 
8, nearMiss));
-        }
-
-        // totalBuckets is deliberately different but is not part of 
FileEntry.Identifier.
-        entries.add(ManifestEntry.create(FileKind.DELETE, partitionA, 9, 32, 
totalBucketsFile));
-
-        List<ManifestFileMeta> manifestMetas =
-                table.store().manifestFileFactory().create().write(entries);
-        Optional<AssignmentPlanView> compactPlan = compactPlanView(table, 
manifestMetas, null);
-        Optional<AssignmentPlanView> legacyPlan = legacyPlanView(table, 
manifestMetas, null);
-
-        assertPlanViewsEqual(compactPlan, legacyPlan);
-        assertThat(compactPlan).isPresent();
-        assertThat(compactPlan.get().partitionMappings).hasSize(1);
-        PartitionMappingView mapping = 
compactPlan.get().partitionMappings.get(0);
-        assertThat(mapping.oldStarts).containsExactly(0L, 2L, 4L);
-        assertThat(mapping.oldEnds).containsExactly(0L, 2L, 4L);
-        assertThat(mapping.newStarts).containsExactly(0L, 1L, 2L);
-        assertThat(compactPlan.get().totalOffset).isEqualTo(3L);
-    }
-
     @Test
     public void testReassignRowIdsByPartition() throws Exception {
         FileStoreTable table = createTableWithInterleavedPartitions();
@@ -1032,7 +627,6 @@ public class DataEvolutionRowIdReassignerTest extends 
TableTestBase {
         }
     }
 
-    @SuppressWarnings("unchecked")
     private void assertPersistedPlan(FileStoreTable table) throws Exception {
         Snapshot snapshot = 
Snapshot.fromJson(table.snapshotManager().latestSnapshot().toJson());
         assertThat(snapshot.properties())
@@ -1049,8 +643,6 @@ public class DataEvolutionRowIdReassignerTest extends 
TableTestBase {
         assertThat(assignment.firstAssignedRowId())
                 .isEqualTo(table.snapshotManager().snapshot(snapshot.id() - 
1).nextRowId());
         assertThat(assignment.nextRowId()).isEqualTo(snapshot.nextRowId());
-        Map<BinaryRow, RowRangeMappingIndex> mappings =
-                (Map<BinaryRow, RowRangeMappingIndex>) fieldValue(assignment, 
"rowIdMappings");
         Map<String, ManifestEntry> previous = new HashMap<>();
         for (ManifestEntry entry :
                 table.store().newScan().withSnapshot(snapshot.id() - 
1).plan().files()) {
@@ -1059,16 +651,7 @@ public class DataEvolutionRowIdReassignerTest extends 
TableTestBase {
         for (ManifestEntry entry : currentEntries(table)) {
             Range oldRange = 
previous.get(entry.file().fileName()).file().nonNullRowIdRange();
             Range newRange = entry.file().nonNullRowIdRange();
-            RowRangeMappingIndex mapping = mappings.get(entry.partition());
-            if (oldRange.equals(newRange)) {
-                if (mapping != null) {
-                    assertThat(mapping.map(oldRange)).isEmpty();
-                    assertThat(mapping.overlaps(oldRange)).isFalse();
-                }
-            } else {
-                assertThat(mapping).isNotNull();
-                assertThat(mapping.map(oldRange)).hasValue(newRange);
-            }
+            assertThat(assignment.mapRowRange(entry.partition(), 
oldRange)).isEqualTo(newRange);
         }
     }
 
@@ -1095,13 +678,6 @@ public class DataEvolutionRowIdReassignerTest extends 
TableTestBase {
                 .containsEntry("pt=a/", Collections.singletonList(0L))
                 .containsEntry("pt=z/", Collections.singletonList(2L));
 
-        Snapshot planningSnapshot = table.snapshotManager().latestSnapshot();
-        List<ManifestFileMeta> planningManifests =
-                
table.store().manifestListFactory().create().readDataManifests(planningSnapshot);
-        assertPlanViewsEqual(
-                compactPlanView(table, planningManifests, null),
-                legacyPlanView(table, planningManifests, null));
-
         DataEvolutionRowIdReassigner.Result result =
                 new DataEvolutionRowIdReassigner(table)
                         .reassign("test-reassign-null-partition-row-id");
@@ -1195,8 +771,10 @@ public class DataEvolutionRowIdReassignerTest extends 
TableTestBase {
                 .containsEntry("pt=b/", Arrays.asList(9L, 10L));
     }
 
-    @Test
-    public void testReassignAbortsForPartiallyOverlappingConcurrentAppend() 
throws Exception {
+    @ParameterizedTest
+    @ValueSource(booleans = {false, true})
+    public void 
testReassignAbortsForPartiallyOverlappingConcurrentAppend(boolean compact)
+            throws Exception {
         FileStoreTable table = createTableWithInterleavedPartitions();
         Snapshot before = table.snapshotManager().latestSnapshot();
 
@@ -1209,6 +787,9 @@ public class DataEvolutionRowIdReassignerTest extends 
TableTestBase {
                                                 () -> {
                                                     if 
(appended.compareAndSet(false, true)) {
                                                         try {
+                                                            if (compact) {
+                                                                
compactManifests(table);
+                                                            }
                                                             
writePartialPayload(
                                                                     table,
                                                                     "a",
@@ -1225,7 +806,8 @@ public class DataEvolutionRowIdReassignerTest extends 
TableTestBase {
                 .hasMessageContaining("partially overlaps planned range");
 
         assertThat(appended).isTrue();
-        
assertThat(table.snapshotManager().latestSnapshot().id()).isEqualTo(before.id() 
+ 1);
+        assertThat(table.snapshotManager().latestSnapshot().id())
+                .isEqualTo(before.id() + (compact ? 2 : 1));
     }
 
     @Test
@@ -1290,11 +872,14 @@ public class DataEvolutionRowIdReassignerTest extends 
TableTestBase {
                 .containsExactly("0|a|v0", "1|b|v1", "2|a|v2", "3|b|v3", 
"4|a|v4");
     }
 
-    @Test
-    public void 
testReassignKeepsHistoricalAddDeleteEntriesConsistentAfterRetry() throws 
Exception {
+    @ParameterizedTest
+    @ValueSource(strings = {"none", "data", "manifest"})
+    public void testReassignKeepsHistoricalEntriesConsistentAfterRetry(String 
compaction)
+            throws Exception {
         FileStoreTable table = createTableWithInterleavedPartitions();
         FileEntry.Identifier compactedFile = compactOneFile(table, "pt=a/");
         long oldFirstRowId = assertAddDeleteEntriesConsistent(table, 
compactedFile);
+        List<String> plannedManifestFiles = dataManifestFileNames(table);
 
         AtomicBoolean appended = new AtomicBoolean();
         new DataEvolutionRowIdReassigner(
@@ -1303,6 +888,13 @@ public class DataEvolutionRowIdReassignerTest extends 
TableTestBase {
                         () -> {
                             if (appended.compareAndSet(false, true)) {
                                 try {
+                                    if (compaction.equals("data")) {
+                                        compactOneFile(table, "pt=a/");
+                                    } else if (compaction.equals("manifest")) {
+                                        compactManifests(table);
+                                        
assertThat(dataManifestFileNames(table))
+                                                
.doesNotContainAnyElementsOf(plannedManifestFiles);
+                                    }
                                     writeOneRow(table, "c", 100);
                                 } catch (Exception e) {
                                     throw new RuntimeException(e);
@@ -1312,8 +904,14 @@ public class DataEvolutionRowIdReassignerTest extends 
TableTestBase {
                 .reassign("test-reassign-historical-add-delete");
 
         assertThat(appended).isTrue();
-        assertThat(assertAddDeleteEntriesConsistent(table, compactedFile))
-                .isNotEqualTo(oldFirstRowId);
+        if (!compaction.equals("manifest")) {
+            assertThat(assertAddDeleteEntriesConsistent(table, compactedFile))
+                    .isNotEqualTo(oldFirstRowId);
+        }
+        assertThat(rowIdsByPartition(table))
+                .containsEntry("pt=a/", Arrays.asList(6L, 7L, 8L))
+                .containsEntry("pt=b/", Arrays.asList(9L, 10L))
+                .containsEntry("pt=c/", Collections.singletonList(5L));
         assertThat(readTableRows(table))
                 .containsExactly("0|a|v0", "100|c|v100", "1|b|v1", "2|a|v2", 
"3|b|v3", "4|a|v4");
     }
@@ -1655,35 +1253,6 @@ public class DataEvolutionRowIdReassignerTest extends 
TableTestBase {
                 .isEqualTo(Snapshot.CommitKind.OVERWRITE);
     }
 
-    @Test
-    public void testReassignAbortsAfterConcurrentManifestCompaction() throws 
Exception {
-        FileStoreTable table = createTableWithInterleavedPartitions();
-        Snapshot before = table.snapshotManager().latestSnapshot();
-
-        AtomicBoolean compacted = new AtomicBoolean();
-        assertThatThrownBy(
-                        () ->
-                                new DataEvolutionRowIdReassigner(
-                                                table,
-                                                null,
-                                                () -> {
-                                                    if 
(compacted.compareAndSet(false, true)) {
-                                                        try {
-                                                            
compactManifests(table);
-                                                        } catch (Exception e) {
-                                                            throw new 
RuntimeException(e);
-                                                        }
-                                                    }
-                                                })
-                                        
.reassign("test-reassign-compact-conflict"))
-                .isInstanceOf(RuntimeException.class)
-                .hasMessageContaining("COMPACT snapshot");
-
-        
assertThat(table.snapshotManager().latestSnapshot().id()).isEqualTo(before.id() 
+ 1);
-        assertThat(table.snapshotManager().latestSnapshot().commitKind())
-                .isEqualTo(Snapshot.CommitKind.COMPACT);
-    }
-
     @Test
     public void testReassignContinuesAfterConcurrentAnalyze() throws Exception 
{
         FileStoreTable table = createTableWithInterleavedPartitions();
@@ -1724,13 +1293,6 @@ public class DataEvolutionRowIdReassignerTest extends 
TableTestBase {
 
         
assertThat(table.snapshotManager().latestSnapshot().nextRowId()).isEqualTo(6L);
 
-        Snapshot planningSnapshot = table.snapshotManager().latestSnapshot();
-        List<ManifestFileMeta> planningManifests =
-                
table.store().manifestListFactory().create().readDataManifests(planningSnapshot);
-        assertPlanViewsEqual(
-                compactPlanView(table, planningManifests, null),
-                legacyPlanView(table, planningManifests, null));
-
         DataEvolutionRowIdReassigner.Result result =
                 new 
DataEvolutionRowIdReassigner(table).reassign("test-reassign-multi-row");
 
@@ -2568,6 +2130,11 @@ public class DataEvolutionRowIdReassignerTest extends 
TableTestBase {
         verifyReassignOutOfOrderPartitionEntries(LARGE_ENTRY_COUNT, 
LARGE_MANIFEST_FILE_COUNT);
     }
 
+    public static DataEvolutionRowIdReassigner reassignerWithBeforeCommit(
+            FileStoreTable table, Runnable beforeCommit) {
+        return new DataEvolutionRowIdReassigner(table, null, beforeCommit);
+    }
+
     private FileStoreTable createTableWithInterleavedPartitions() throws 
Exception {
         createTableDefault();
         FileStoreTable table = getTableDefault();
@@ -2687,12 +2254,6 @@ public class DataEvolutionRowIdReassignerTest extends 
TableTestBase {
         ExpectedLargeManifestAssignment expectedAssignment =
                 expectedLargeManifestAssignment(table, before, entryCount);
 
-        List<ManifestFileMeta> planningManifests =
-                
table.store().manifestListFactory().create().readDataManifests(before);
-        assertPlanViewsEqual(
-                compactPlanView(table, planningManifests, null),
-                legacyPlanView(table, planningManifests, null));
-
         DataEvolutionRowIdReassigner.Result result =
                 new DataEvolutionRowIdReassigner(table)
                         .reassign("test-reassign-large-out-of-order-row-id");
@@ -2856,49 +2417,6 @@ public class DataEvolutionRowIdReassignerTest extends 
TableTestBase {
         }
     }
 
-    private DataFileMeta planningDataFile(
-            String fileName,
-            long firstRowId,
-            int level,
-            List<String> extraFiles,
-            byte[] embeddedIndex,
-            String externalPath) {
-        return DataFileMeta.forAppend(
-                        fileName,
-                        1L,
-                        1L,
-                        SimpleStats.EMPTY_STATS,
-                        0L,
-                        0L,
-                        0L,
-                        extraFiles,
-                        embeddedIndex,
-                        FileSource.APPEND,
-                        null,
-                        externalPath,
-                        firstRowId,
-                        null)
-                .upgrade(level);
-    }
-
-    private ManifestFileMeta withoutPartitionStats(ManifestFileMeta 
manifestMeta) {
-        return new ManifestFileMeta(
-                manifestMeta.fileName(),
-                manifestMeta.fileSize(),
-                manifestMeta.numAddedFiles(),
-                manifestMeta.numDeletedFiles(),
-                null,
-                manifestMeta.schemaId(),
-                manifestMeta.minBucket(),
-                manifestMeta.maxBucket(),
-                manifestMeta.minLevel(),
-                manifestMeta.maxLevel(),
-                manifestMeta.minRowId(),
-                manifestMeta.maxRowId(),
-                null,
-                null);
-    }
-
     private void assertReassignedOutOfOrderPartitionEntries(
             FileStoreTable table,
             int entryCount,
@@ -3794,180 +3312,6 @@ public class DataEvolutionRowIdReassignerTest extends 
TableTestBase {
         return result;
     }
 
-    private Optional<AssignmentPlanView> compactPlanView(
-            FileStoreTable table,
-            List<ManifestFileMeta> manifestMetas,
-            PartitionPredicate partitionPredicate)
-            throws Exception {
-        Method planAssignment =
-                
DataEvolutionRowIdReassigner.class.getDeclaredMethod("planAssignment", 
List.class);
-        planAssignment.setAccessible(true);
-        Object result =
-                planAssignment.invoke(
-                        new DataEvolutionRowIdReassigner(table, 
partitionPredicate), manifestMetas);
-        return assignmentPlanView(result, manifestMetas);
-    }
-
-    private Optional<AssignmentPlanView> legacyPlanView(
-            FileStoreTable table,
-            List<ManifestFileMeta> manifestMetas,
-            PartitionPredicate partitionPredicate)
-            throws Exception {
-        Method planAssignment =
-                LegacyDataEvolutionRowIdReassigner.class.getDeclaredMethod(
-                        "planAssignment", List.class, ManifestFile.class);
-        planAssignment.setAccessible(true);
-        Object result =
-                planAssignment.invoke(
-                        new LegacyDataEvolutionRowIdReassigner(table, 
partitionPredicate),
-                        manifestMetas,
-                        table.store().manifestFileFactory().create());
-        return assignmentPlanView(result, manifestMetas);
-    }
-
-    private Optional<AssignmentPlanView> assignmentPlanView(
-            Object optionalResult, List<ManifestFileMeta> manifestMetas) 
throws Exception {
-        assertThat(optionalResult).isInstanceOf(Optional.class);
-        Optional<?> optionalPlan = (Optional<?>) optionalResult;
-        if (!optionalPlan.isPresent()) {
-            return Optional.empty();
-        }
-
-        Object assignmentPlan = optionalPlan.get();
-        @SuppressWarnings("unchecked")
-        List<ManifestFileMeta> rewrittenManifests =
-                (List<ManifestFileMeta>) fieldValue(assignmentPlan, 
"manifestMetasToRewrite");
-        int[] manifestOrdinals = new int[rewrittenManifests.size()];
-        for (int i = 0; i < rewrittenManifests.size(); i++) {
-            manifestOrdinals[i] = manifestOrdinal(manifestMetas, 
rewrittenManifests.get(i));
-        }
-
-        Object relativeMappings = fieldValue(assignmentPlan, 
"relativeRowIdMappings");
-        @SuppressWarnings("unchecked")
-        Map<BinaryRow, RowRangeMappingIndex> mappings =
-                (Map<BinaryRow, RowRangeMappingIndex>) 
fieldValue(relativeMappings, "mappings");
-        List<PartitionMappingView> partitionMappings = new 
ArrayList<>(mappings.size());
-        for (Map.Entry<BinaryRow, RowRangeMappingIndex> mapping : 
mappings.entrySet()) {
-            partitionMappings.add(
-                    new PartitionMappingView(
-                            mapping.getKey().toBytes(),
-                            longArrayField(mapping.getValue(), "oldStarts"),
-                            longArrayField(mapping.getValue(), "oldEnds"),
-                            longArrayField(mapping.getValue(), "newStarts"),
-                            (Long) fieldValue(mapping.getValue(), 
"newStartOffset")));
-        }
-        long totalOffset = (Long) fieldValue(relativeMappings, "totalOffset");
-        return Optional.of(
-                new AssignmentPlanView(manifestOrdinals, partitionMappings, 
totalOffset));
-    }
-
-    private void assertPlanViewsEqual(
-            Optional<AssignmentPlanView> compact, Optional<AssignmentPlanView> 
legacy) {
-        assertThat(compact.isPresent()).isEqualTo(legacy.isPresent());
-        if (!compact.isPresent()) {
-            return;
-        }
-
-        AssignmentPlanView compactPlan = compact.get();
-        AssignmentPlanView legacyPlan = legacy.get();
-        
assertThat(compactPlan.manifestOrdinals).containsExactly(legacyPlan.manifestOrdinals);
-        assertThat(compactPlan.totalOffset).isEqualTo(legacyPlan.totalOffset);
-        
assertThat(compactPlan.partitionMappings).hasSameSizeAs(legacyPlan.partitionMappings);
-        for (int i = 0; i < compactPlan.partitionMappings.size(); i++) {
-            PartitionMappingView compactMapping = 
compactPlan.partitionMappings.get(i);
-            PartitionMappingView legacyMapping = 
legacyPlan.partitionMappings.get(i);
-            
assertThat(compactMapping.partitionBytes).containsExactly(legacyMapping.partitionBytes);
-            
assertThat(compactMapping.oldStarts).containsExactly(legacyMapping.oldStarts);
-            
assertThat(compactMapping.oldEnds).containsExactly(legacyMapping.oldEnds);
-            
assertThat(compactMapping.newStarts).containsExactly(legacyMapping.newStarts);
-            
assertThat(compactMapping.newStartOffset).isEqualTo(legacyMapping.newStartOffset);
-        }
-    }
-
-    private int[] manifestOrdinalsContaining(
-            FileStoreTable table,
-            List<ManifestFileMeta> manifestMetas,
-            FileEntry.Identifier identifier) {
-        ManifestFile manifestFile = 
table.store().manifestFileFactory().create();
-        List<Integer> ordinals = new ArrayList<>();
-        for (int i = 0; i < manifestMetas.size(); i++) {
-            ManifestFileMeta manifestMeta = manifestMetas.get(i);
-            for (ManifestEntry entry :
-                    manifestFile.read(manifestMeta.fileName(), 
manifestMeta.fileSize())) {
-                if (entry.identifier().equals(identifier)) {
-                    ordinals.add(i);
-                    break;
-                }
-            }
-        }
-        int[] result = new int[ordinals.size()];
-        for (int i = 0; i < ordinals.size(); i++) {
-            result[i] = ordinals.get(i);
-        }
-        return result;
-    }
-
-    private static int manifestOrdinal(
-            List<ManifestFileMeta> manifestMetas, ManifestFileMeta 
plannedManifest) {
-        for (int i = 0; i < manifestMetas.size(); i++) {
-            if 
(manifestMetas.get(i).fileName().equals(plannedManifest.fileName())) {
-                return i;
-            }
-        }
-        throw new AssertionError(
-                "Planned manifest is not present in the source snapshot: "
-                        + plannedManifest.fileName());
-    }
-
-    private static long[] longArrayField(Object target, String fieldName) 
throws Exception {
-        long[] values = (long[]) fieldValue(target, fieldName);
-        return Arrays.copyOf(values, values.length);
-    }
-
-    private static Object fieldValue(Object target, String fieldName) throws 
Exception {
-        Field field = target.getClass().getDeclaredField(fieldName);
-        field.setAccessible(true);
-        return field.get(target);
-    }
-
-    private static class AssignmentPlanView {
-
-        private final int[] manifestOrdinals;
-        private final List<PartitionMappingView> partitionMappings;
-        private final long totalOffset;
-
-        private AssignmentPlanView(
-                int[] manifestOrdinals,
-                List<PartitionMappingView> partitionMappings,
-                long totalOffset) {
-            this.manifestOrdinals = manifestOrdinals;
-            this.partitionMappings = partitionMappings;
-            this.totalOffset = totalOffset;
-        }
-    }
-
-    private static class PartitionMappingView {
-
-        private final byte[] partitionBytes;
-        private final long[] oldStarts;
-        private final long[] oldEnds;
-        private final long[] newStarts;
-        private final long newStartOffset;
-
-        private PartitionMappingView(
-                byte[] partitionBytes,
-                long[] oldStarts,
-                long[] oldEnds,
-                long[] newStarts,
-                long newStartOffset) {
-            this.partitionBytes = partitionBytes;
-            this.oldStarts = oldStarts;
-            this.oldEnds = oldEnds;
-            this.newStarts = newStarts;
-            this.newStartOffset = newStartOffset;
-        }
-    }
-
     private static class ExpectedLargeManifestAssignment {
 
         private final int[] entriesByPartition;
diff --git 
a/paimon-core/src/test/java/org/apache/paimon/table/DataEvolutionDeletionVectorTest.java
 
b/paimon-core/src/test/java/org/apache/paimon/table/DataEvolutionDeletionVectorTest.java
index 1ec1343ab1..a0c2afa26c 100644
--- 
a/paimon-core/src/test/java/org/apache/paimon/table/DataEvolutionDeletionVectorTest.java
+++ 
b/paimon-core/src/test/java/org/apache/paimon/table/DataEvolutionDeletionVectorTest.java
@@ -50,7 +50,6 @@ import org.apache.paimon.manifest.FileKind;
 import org.apache.paimon.manifest.IndexManifestEntry;
 import org.apache.paimon.manifest.ManifestEntry;
 import org.apache.paimon.manifest.ManifestFileMeta;
-import org.apache.paimon.partition.PartitionPredicate;
 import org.apache.paimon.reader.RecordReader;
 import org.apache.paimon.schema.Schema;
 import org.apache.paimon.schema.SchemaChange;
@@ -77,7 +76,6 @@ import org.junit.jupiter.params.ParameterizedTest;
 import org.junit.jupiter.params.provider.ValueSource;
 
 import java.io.IOException;
-import java.lang.reflect.Constructor;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
@@ -89,6 +87,7 @@ import java.util.Set;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.stream.Collectors;
 
+import static 
org.apache.paimon.append.dataevolution.DataEvolutionRowIdReassignerTest.reassignerWithBeforeCommit;
 import static 
org.apache.paimon.deletionvectors.DeletionVectorsIndexFile.DELETION_VECTORS_INDEX;
 import static 
org.apache.paimon.errors.ErrorMessages.DATA_EVOLUTION_ROW_ID_CONFLICT_MESSAGE;
 import static org.apache.paimon.format.blob.BlobFileFormat.isBlobFile;
@@ -189,6 +188,49 @@ public class DataEvolutionDeletionVectorTest extends 
DataEvolutionTestBase {
                 .containsExactly("a|0|0", "a|2|2", "b|4|4", "a|5|5", "a|7|7");
     }
 
+    @Test
+    public void testRowIdReassignAcrossConcurrentDeletionMaterialization() 
throws Exception {
+        FileStoreTable table = 
createPartitionedReassignTable("reassign_materialize_dv", true);
+        writeDedicatedPartitionRows(table, "a", 0, 1, 2);
+        writeDedicatedPartitionRows(table, "b", 3, 4);
+        writeDedicatedPartitionRows(table, "a", 5, 6, 7);
+        writePartialStrings(table, "a", 0, 100, 101, 102);
+        commitDeletionVectors(
+                table,
+                partition(table, "a"),
+                Collections.singletonList(new DvSpec(new Range(0, 2), 1)));
+
+        AtomicBoolean materialized = new AtomicBoolean();
+        DataEvolutionRowIdReassigner.Result result =
+                reassignerWithBeforeCommit(
+                                table,
+                                () -> {
+                                    if (materialized.compareAndSet(false, 
true)) {
+                                        try {
+                                            materializeDeletionVectors(table);
+                                        } catch (Exception e) {
+                                            throw new RuntimeException(e);
+                                        }
+                                    }
+                                })
+                        .reassign("test-reassign-concurrent-materialize");
+
+        assertThat(materialized).isTrue();
+        assertThat(result.reassigned).isTrue();
+        assertThat(result.firstAssignedRowId).isEqualTo(10L);
+        assertThat(result.nextRowId).isEqualTo(16L);
+        // Materialized files retain their new IDs; surviving old files keep 
the plan's offsets.
+        assertThat(readPartitionedRowsWithRowIds(table))
+                .containsExactly(
+                        "b|3|base-3|3|3",
+                        "b|4|base-4|4|4",
+                        "a|0|updated-100|0|8",
+                        "a|2|updated-102|2|9",
+                        "a|5|base-5|5|13",
+                        "a|6|base-6|6|14",
+                        "a|7|base-7|7|15");
+    }
+
     @Test
     public void testRowIdReassignKeepsPartialWriteAndBlobFileOffsets() throws 
Exception {
         FileStoreTable table = 
createPartitionedReassignTable("reassign_dedicated_dv_table", true);
@@ -1286,15 +1328,6 @@ public class DataEvolutionDeletionVectorTest extends 
DataEvolutionTestBase {
                 .collect(Collectors.toList());
     }
 
-    private static DataEvolutionRowIdReassigner reassignerWithBeforeCommit(
-            FileStoreTable table, Runnable beforeCommit) throws Exception {
-        Constructor<DataEvolutionRowIdReassigner> constructor =
-                DataEvolutionRowIdReassigner.class.getDeclaredConstructor(
-                        FileStoreTable.class, PartitionPredicate.class, 
Runnable.class);
-        constructor.setAccessible(true);
-        return constructor.newInstance(table, null, beforeCommit);
-    }
-
     @Override
     protected Schema schemaDefault() {
         Schema.Builder schemaBuilder = Schema.newBuilder();

Reply via email to