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

yiguolei pushed a commit to branch branch-4.1
in repository https://gitbox.apache.org/repos/asf/doris.git


The following commit(s) were added to refs/heads/branch-4.1 by this push:
     new 98a0367df00 branch-4.1: [fix](fe) Use concurrent map for OlapTable 
partition names to avoid ConcurrentModificationException #69166 (#69267)
98a0367df00 is described below

commit 98a0367df0066b30fdb429274aaf81dbd7e1c8ae
Author: yujun <[email protected]>
AuthorDate: Tue Oct 20 09:57:32 2026 +0800

    branch-4.1: [fix](fe) Use concurrent map for OlapTable partition names to 
avoid ConcurrentModificationException #69166 (#69267)
    
    cherry-pick: #69166
---
 .../java/org/apache/doris/catalog/OlapTable.java   |  7 +++++--
 .../org/apache/doris/catalog/OlapTableTest.java    | 24 ++++++++++++++++++++++
 2 files changed, 29 insertions(+), 2 deletions(-)

diff --git a/fe/fe-core/src/main/java/org/apache/doris/catalog/OlapTable.java 
b/fe/fe-core/src/main/java/org/apache/doris/catalog/OlapTable.java
index f319735e1d1..e341187152f 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/catalog/OlapTable.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/catalog/OlapTable.java
@@ -127,6 +127,7 @@ import java.util.Set;
 import java.util.TreeMap;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentSkipListMap;
 import java.util.concurrent.atomic.AtomicLong;
 import java.util.concurrent.locks.ReadWriteLock;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
@@ -190,9 +191,11 @@ public class OlapTable extends Table implements 
MTMVRelatedTableIf, GsonPostProc
     @SerializedName(value = "itp", alternate = {"idToPartition"})
     @Getter
     protected ConcurrentHashMap<Long, Partition> idToPartition = new 
ConcurrentHashMap<>();
-    // handled in postgsonprocess
+    // handled in postgsonprocess. Sorted, and weakly consistent so that 
lock-free readers
+    // (e.g. EXPLAIN plan rendering) never hit ConcurrentModificationException 
while the table
+    // write lock holder adds/drops partitions.
     @Getter
-    protected Map<String, Partition> nameToPartition = Maps.newTreeMap();
+    protected Map<String, Partition> nameToPartition = new 
ConcurrentSkipListMap<>();
 
     @SerializedName(value = "di", alternate = {"distributionInfo"})
     private DistributionInfo defaultDistributionInfo;
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/catalog/OlapTableTest.java 
b/fe/fe-core/src/test/java/org/apache/doris/catalog/OlapTableTest.java
index 3a10a7dc162..6681f039588 100644
--- a/fe/fe-core/src/test/java/org/apache/doris/catalog/OlapTableTest.java
+++ b/fe/fe-core/src/test/java/org/apache/doris/catalog/OlapTableTest.java
@@ -52,6 +52,7 @@ import java.io.DataOutputStream;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -84,6 +85,29 @@ public class OlapTableTest {
         Assert.assertEquals(132L, stats.getIndexLength());
     }
 
+    @Test
+    public void testPartitionNameMapToleratesConcurrentAdd() {
+        OlapTable olapTable = new OlapTable();
+        for (int i = 0; i < 3; i++) {
+            long partitionId = 10 + i;
+            MaterializedIndex index = new MaterializedIndex(partitionId, 
MaterializedIndex.IndexState.NORMAL);
+            olapTable.addPartition(new Partition(partitionId, "p" + i, index, 
new RandomDistributionInfo(1)));
+        }
+
+        // getPartitionNames() copies nameToPartition's keys without holding 
any lock, while a writer
+        // holding the table write lock may add a partition at the same time, 
so the iteration must be
+        // weakly consistent instead of fail fast.
+        Iterator<String> iterator = 
olapTable.getNameToPartition().keySet().iterator();
+        Assert.assertTrue(iterator.hasNext());
+        iterator.next();
+        MaterializedIndex newIndex = new MaterializedIndex(20, 
MaterializedIndex.IndexState.NORMAL);
+        olapTable.addPartition(new Partition(20, "p10", newIndex, new 
RandomDistributionInfo(1)));
+        while (iterator.hasNext()) {
+            iterator.next();
+        }
+        Assert.assertEquals(4, olapTable.getPartitionNames().size());
+    }
+
     private Replica mockReplica(Replica.ReplicaState state, long dataSize, 
long localSegmentSize,
             long remoteSegmentSize, long localIndexSize, long remoteIndexSize) 
{
         Replica replica = Mockito.mock(Replica.class);


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to