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 e69f225b92a branch-4.1: [fix](fe) Guard auto partition result against 
concurrent drops #65282 (#65956)
e69f225b92a is described below

commit e69f225b92a32195f856205dba5ad3d5c7466472
Author: github-actions[bot] 
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Fri Jul 24 11:09:11 2026 +0800

    branch-4.1: [fix](fe) Guard auto partition result against concurrent drops 
#65282 (#65956)
    
    Cherry-picked from #65282
    
    Co-authored-by: zclllyybb <[email protected]>
---
 .../apache/doris/service/FrontendServiceImpl.java  | 438 ++++++++++++---------
 .../doris/service/FrontendServiceImplTest.java     |  49 +++
 2 files changed, 303 insertions(+), 184 deletions(-)

diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/service/FrontendServiceImpl.java 
b/fe/fe-core/src/main/java/org/apache/doris/service/FrontendServiceImpl.java
index b6f87e27f51..2b717f30e04 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/service/FrontendServiceImpl.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/service/FrontendServiceImpl.java
@@ -38,6 +38,7 @@ import org.apache.doris.catalog.MaterializedIndex;
 import org.apache.doris.catalog.OlapTable;
 import org.apache.doris.catalog.Partition;
 import org.apache.doris.catalog.PartitionInfo;
+import org.apache.doris.catalog.PartitionItem;
 import org.apache.doris.catalog.PartitionType;
 import org.apache.doris.catalog.Replica;
 import org.apache.doris.catalog.Table;
@@ -3956,26 +3957,6 @@ public class FrontendServiceImpl implements 
FrontendService.Iface {
             }
         }
 
-        // check partition's number limit. because partitions in 
addPartitionClauseMap may be duplicated with existing
-        // partitions, which would lead to false positive. so we should check 
the partition number AFTER adding new
-        // partitions using its ACTUAL NUMBER, rather than the sum of existing 
and requested partitions.
-        int partitionNum = olapTable.getPartitionNum();
-        int autoPartitionLimit = Config.max_auto_partition_num;
-        if (partitionNum > autoPartitionLimit) {
-            String errorMessage = String.format(
-                    "partition numbers %d exceeded limit of variable 
max_auto_partition_num %d",
-                    partitionNum, autoPartitionLimit);
-            LOG.warn(errorMessage);
-            errorStatus.setErrorMsgs(Lists.newArrayList(errorMessage));
-            result.setStatus(errorStatus);
-            LOG.warn("send create partition error status: {}", result);
-            return result;
-        } else if (partitionNum > autoPartitionLimit * 8 / 10) {
-            LOG.warn("Table {}.{} auto partition count {} is approaching limit 
{} (>80%)."
-                        + " Consider increasing max_auto_partition_num.",
-                    db.getFullName(), olapTable.getName(), partitionNum, 
autoPartitionLimit);
-        }
-
         // build partition & tablets
         List<TTabletLocation> tablets = new ArrayList<>();
         List<TTabletLocation> slaveTablets = new ArrayList<>();
@@ -3987,41 +3968,59 @@ public class FrontendServiceImpl implements 
FrontendService.Iface {
                 && request.isEnableAdaptiveRandomBucket();
         boolean loadToSingleTablet = request.isSetLoadToSingleTablet() && 
request.isLoadToSingleTablet();
         final boolean hasBeEndpoint = request.isSetBeEndpoint();
-        // Lazy: resolved on the first CloudTablet that needs it (skipped on 
cache-hit).
-        String cachedClusterId = null;
-        for (String partitionName : addPartitionClauseMap.keySet()) {
-            Partition partition = table.getPartition(partitionName);
-            // For thread safety, we preserve the tablet distribution 
information of each partition
-            // before calling getOrSetAutoPartitionInfo, but not check the 
partition first
-            List<TTabletLocation> partitionTablets = new ArrayList<>();
-            List<TTabletLocation> partitionSlaveTablets = new ArrayList<>();
-            TOlapTablePartition tPartition = new TOlapTablePartition();
-            tPartition.setId(partition.getId());
-            int partColNum = partitionInfo.getPartitionColumns().size();
+        List<PartitionResultSnapshot> partitionSnapshots = new ArrayList<>();
+
+        olapTable.readLock();
+        try {
+            // check partition's number limit. because partitions in 
addPartitionClauseMap may be duplicated with
+            // existing partitions, which would lead to false positive. so we 
should check the partition number AFTER
+            // adding new partitions using its ACTUAL NUMBER, rather than the 
sum of existing and requested partitions.
+            int partitionNum = olapTable.getPartitionNum();
+            int autoPartitionLimit = Config.max_auto_partition_num;
+            if (partitionNum > autoPartitionLimit) {
+                String errorMessage = String.format(
+                        "partition numbers %d exceeded limit of variable 
max_auto_partition_num %d",
+                        partitionNum, autoPartitionLimit);
+                LOG.warn(errorMessage);
+                errorStatus.setErrorMsgs(Lists.newArrayList(errorMessage));
+                result.setStatus(errorStatus);
+                LOG.warn("send create partition error status: {}", result);
+                return result;
+            } else if (partitionNum > autoPartitionLimit * 8 / 10) {
+                LOG.warn("Table {}.{} auto partition count {} is approaching 
limit {} (>80%)."
+                            + " Consider increasing max_auto_partition_num.",
+                        db.getFullName(), olapTable.getName(), partitionNum, 
autoPartitionLimit);
+            }
+
             try {
-                OlapTableSink.setPartitionKeys(tPartition, 
partitionInfo.getItem(partition.getId()), partColNum);
+                
partitionSnapshots.addAll(snapshotPartitionResultsByName(olapTable, 
addPartitionClauseMap.keySet(),
+                        loadToSingleTablet, enableAdaptiveRandomBucket, "auto 
partition"));
             } catch (UserException ex) {
                 errorStatus.setErrorMsgs(Lists.newArrayList(ex.getMessage()));
                 result.setStatus(errorStatus);
                 LOG.warn("send create partition error status: {}", result);
                 return result;
             }
-            for (MaterializedIndex index : 
partition.getMaterializedIndices(MaterializedIndex.IndexExtState.ALL)) {
-                tPartition.addToIndexes(new 
TOlapTableIndexTablets(index.getId(), Lists.newArrayList(
-                        
index.getTablets().stream().map(Tablet::getId).collect(Collectors.toList()))));
-                tPartition.setNumBuckets(index.getTablets().size());
-            }
-            
tPartition.setIsMutable(olapTable.getPartitionInfo().getIsMutable(partition.getId()));
-            boolean randomDistribution =
-                    partition.getDistributionInfo().getType() == 
DistributionInfo.DistributionInfoType.RANDOM;
-            boolean cacheLoadTabletIdx =
-                    (loadToSingleTablet || enableAdaptiveRandomBucket) && 
randomDistribution;
+        } finally {
+            olapTable.readUnlock();
+        }
+
+        // Lazy: resolved on the first CloudTablet that needs it (skipped on 
cache-hit).
+        String cachedClusterId = null;
+        for (PartitionResultSnapshot partitionSnapshot : partitionSnapshots) {
+            Partition partition = partitionSnapshot.partition;
+            long partitionId = partitionSnapshot.partitionId;
+            TOlapTablePartition tPartition = partitionSnapshot.tPartition;
+            boolean cacheLoadTabletIdx = partitionSnapshot.cacheLoadTabletIdx;
             partitions.add(tPartition);
-            // tablet
+            // For thread safety, we preserve the tablet distribution 
information of each partition
+            // before calling getOrSetAutoPartitionInfo, but not check the 
partition first
+            List<TTabletLocation> partitionTablets = new ArrayList<>();
+            List<TTabletLocation> partitionSlaveTablets = new ArrayList<>();
             AtomicLong cachedLoadTabletIdx = new AtomicLong(-1);
             if (needUseCache
                     && 
Env.getCurrentGlobalTransactionMgr().getAutoPartitionCacheMgr()
-                            .getAutoPartitionInfo(txnId, partition.getId(), 
partitionTablets,
+                            .getAutoPartitionInfo(txnId, partitionId, 
partitionTablets,
                                     partitionSlaveTablets, 
cachedLoadTabletIdx)) {
                 if (cacheLoadTabletIdx) {
                     tPartition.setLoadTabletIdx(cachedLoadTabletIdx.get());
@@ -4045,52 +4044,49 @@ public class FrontendServiceImpl implements 
FrontendService.Iface {
                     return result;
                 }
             }
-            int quorum = 
olapTable.getPartitionInfo().getReplicaAllocation(partition.getId()).getTotalReplicaNum()
 / 2
-                    + 1;
-            for (MaterializedIndex index : 
partition.getMaterializedIndices(MaterializedIndex.IndexExtState.ALL)) {
-                for (Tablet tablet : index.getTablets()) {
-                    // we should ensure the replica backend is alive
-                    // otherwise, there will be a 'unknown node id, id=xxx' 
error for stream load
-                    // BE id -> path hash
-                    Multimap<Long, Long> bePathsMap;
-                    try {
-                        if (tablet instanceof CloudTablet) {
-                            CloudTablet cloudTablet = (CloudTablet) tablet;
-                            if (hasBeEndpoint) {
-                                bePathsMap = 
cloudTablet.getNormalReplicaBackendPathMap(request.be_endpoint);
-                            } else {
-                                if (cachedClusterId == null) {
-                                    cachedClusterId = 
((CloudSystemInfoService) Env.getCurrentSystemInfo())
-                                            .getCurrentClusterId();
-                                }
-                                bePathsMap = 
cloudTablet.getNormalReplicaBackendPathMapByClusterId(cachedClusterId);
-                            }
+            int quorum = partitionSnapshot.quorum;
+            for (Tablet tablet : partitionSnapshot.tablets) {
+                // we should ensure the replica backend is alive
+                // otherwise, there will be a 'unknown node id, id=xxx' error 
for stream load
+                // BE id -> path hash
+                Multimap<Long, Long> bePathsMap;
+                try {
+                    if (tablet instanceof CloudTablet) {
+                        CloudTablet cloudTablet = (CloudTablet) tablet;
+                        if (hasBeEndpoint) {
+                            bePathsMap = 
cloudTablet.getNormalReplicaBackendPathMap(request.be_endpoint);
                         } else {
-                            bePathsMap = 
tablet.getNormalReplicaBackendPathMap();
+                            if (cachedClusterId == null) {
+                                cachedClusterId = ((CloudSystemInfoService) 
Env.getCurrentSystemInfo())
+                                        .getCurrentClusterId();
+                            }
+                            bePathsMap = 
cloudTablet.getNormalReplicaBackendPathMapByClusterId(cachedClusterId);
                         }
-                    } catch (UserException ex) {
-                        
errorStatus.setErrorMsgs(Lists.newArrayList(ex.getMessage()));
-                        result.setStatus(errorStatus);
-                        LOG.warn("send create partition error status: {}", 
result);
-                        return result;
-                    }
-                    if (bePathsMap.keySet().size() < quorum) {
-                        LOG.warn("auto go quorum exception");
-                    }
-                    if (request.isSetWriteSingleReplica() && 
request.isWriteSingleReplica()) {
-                        Long[] nodes = bePathsMap.keySet().toArray(new 
Long[0]);
-                        Random random = new SecureRandom();
-                        Long masterNode = nodes[random.nextInt(nodes.length)];
-                        Multimap<Long, Long> slaveBePathsMap = bePathsMap;
-                        slaveBePathsMap.removeAll(masterNode);
-                        partitionTablets.add(new 
TTabletLocation(tablet.getId(),
-                                
Lists.newArrayList(Sets.newHashSet(masterNode))));
-                        partitionSlaveTablets.add(new 
TTabletLocation(tablet.getId(),
-                                Lists.newArrayList(slaveBePathsMap.keySet())));
                     } else {
-                        partitionTablets.add(new 
TTabletLocation(tablet.getId(),
-                                Lists.newArrayList(bePathsMap.keySet())));
+                        bePathsMap = tablet.getNormalReplicaBackendPathMap();
                     }
+                } catch (UserException ex) {
+                    
errorStatus.setErrorMsgs(Lists.newArrayList(ex.getMessage()));
+                    result.setStatus(errorStatus);
+                    LOG.warn("send create partition error status: {}", result);
+                    return result;
+                }
+                if (bePathsMap.keySet().size() < quorum) {
+                    LOG.warn("auto go quorum exception");
+                }
+                if (request.isSetWriteSingleReplica() && 
request.isWriteSingleReplica()) {
+                    Long[] nodes = bePathsMap.keySet().toArray(new Long[0]);
+                    Random random = new SecureRandom();
+                    Long masterNode = nodes[random.nextInt(nodes.length)];
+                    Multimap<Long, Long> slaveBePathsMap = bePathsMap;
+                    slaveBePathsMap.removeAll(masterNode);
+                    partitionTablets.add(new TTabletLocation(tablet.getId(),
+                            Lists.newArrayList(Sets.newHashSet(masterNode))));
+                    partitionSlaveTablets.add(new 
TTabletLocation(tablet.getId(),
+                            Lists.newArrayList(slaveBePathsMap.keySet())));
+                } else {
+                    partitionTablets.add(new TTabletLocation(tablet.getId(),
+                            Lists.newArrayList(bePathsMap.keySet())));
                 }
             }
 
@@ -4118,7 +4114,7 @@ public class FrontendServiceImpl implements 
FrontendService.Iface {
             if (needUseCache) {
                 long loadTabletIdx = cacheLoadTabletIdx ? 
tPartition.getLoadTabletIdx() : -1;
                 long cachedTabletIdx = 
Env.getCurrentGlobalTransactionMgr().getAutoPartitionCacheMgr()
-                        .getOrSetAutoPartitionInfo(txnId, partition.getId(), 
partitionTablets,
+                        .getOrSetAutoPartitionInfo(txnId, partitionId, 
partitionTablets,
                                 partitionSlaveTablets, loadTabletIdx);
                 if (cacheLoadTabletIdx) {
                     tPartition.setLoadTabletIdx(cachedTabletIdx);
@@ -4241,6 +4237,14 @@ public class FrontendServiceImpl implements 
FrontendService.Iface {
             }
         }
 
+        Backend requestBackend = request.isSetBeEndpoint() ? 
resolveBeEndpoint(request.getBeEndpoint()) : null;
+        long adaptiveBucketBeId = requestBackend != null ? 
requestBackend.getId() : -1L;
+        TUniqueId queryId = request.isSetQueryId() ? request.getQueryId() : 
null;
+        boolean enableAdaptiveRandomBucket = 
request.isSetEnableAdaptiveRandomBucket()
+                && request.isEnableAdaptiveRandomBucket();
+        boolean loadToSingleTablet = request.isSetLoadToSingleTablet() && 
request.isLoadToSingleTablet();
+        final boolean replaceHasBeEndpoint = request.isSetBeEndpoint();
+
         InsertOverwriteManager overwriteManager = 
Env.getCurrentEnv().getInsertOverwriteManager();
         ReentrantLock taskLock = overwriteManager.getLock(taskGroupId);
         if (taskLock == null) {
@@ -4255,6 +4259,7 @@ public class FrontendServiceImpl implements 
FrontendService.Iface {
         ArrayList<Long> pendingPartitionIds = new ArrayList<>(); // pending: 
[1 2]
         ArrayList<Long> newPartitionIds = new ArrayList<>(); // requested temp 
partition ids. for [7 8]
         boolean needReplace = false;
+        List<PartitionResultSnapshot> partitionSnapshots = new ArrayList<>();
         try {
             taskLock.lock();
             // double check lock. maybe taskLock is not null, but has been 
removed from the Map. means the task failed.
@@ -4302,29 +4307,37 @@ public class FrontendServiceImpl implements 
FrontendService.Iface {
                     }
                 }
             }
-        } catch (DdlException | RuntimeException ex) {
-            errorStatus.setErrorMsgs(Lists.newArrayList(ex.getMessage()));
-            result.setStatus(errorStatus);
-            LOG.warn("send create partition error status: {}", result);
-            return result;
-        } finally {
-            taskLock.unlock();
-        }
 
-        // result: [1 2 5 6], make it [7 8 5 6]
-        int idx = 0;
-        if (needReplace) {
-            for (int i = 0; i < reqPartitionIds.size(); i++) {
-                if (reqPartitionIds.get(i).equals(resultPartitionIds.get(i))) {
-                    resultPartitionIds.set(i, newPartitionIds.get(idx++));
+            // result: [1 2 5 6], make it [7 8 5 6]
+            int idx = 0;
+            if (needReplace) {
+                for (int i = 0; i < reqPartitionIds.size(); i++) {
+                    if 
(reqPartitionIds.get(i).equals(resultPartitionIds.get(i))) {
+                        resultPartitionIds.set(i, newPartitionIds.get(idx++));
+                    }
                 }
             }
-        }
-        if (idx != newPartitionIds.size()) {
-            errorStatus.addToErrorMsgs("changed partition number " + idx + " 
is not correct");
+            if (idx != newPartitionIds.size()) {
+                errorStatus.addToErrorMsgs("changed partition number " + idx + 
" is not correct");
+                result.setStatus(errorStatus);
+                LOG.warn("send create partition error status: {}", result);
+                return result;
+            }
+
+            olapTable.readLock();
+            try {
+                
partitionSnapshots.addAll(snapshotPartitionResultsById(olapTable, 
resultPartitionIds,
+                        loadToSingleTablet, enableAdaptiveRandomBucket, 
"replace partition"));
+            } finally {
+                olapTable.readUnlock();
+            }
+        } catch (UserException | RuntimeException ex) {
+            errorStatus.setErrorMsgs(Lists.newArrayList(ex.getMessage()));
             result.setStatus(errorStatus);
             LOG.warn("send create partition error status: {}", result);
             return result;
+        } finally {
+            taskLock.unlock();
         }
 
         if (LOG.isDebugEnabled()) {
@@ -4341,51 +4354,23 @@ public class FrontendServiceImpl implements 
FrontendService.Iface {
         List<TOlapTablePartition> partitions = new ArrayList<>();
         List<TTabletLocation> tablets = new ArrayList<>();
         List<TTabletLocation> slaveTablets = new ArrayList<>();
-        PartitionInfo partitionInfo = olapTable.getPartitionInfo();
-        Backend requestBackend = request.isSetBeEndpoint() ? 
resolveBeEndpoint(request.getBeEndpoint()) : null;
-        long adaptiveBucketBeId = requestBackend != null ? 
requestBackend.getId() : -1L;
-        TUniqueId queryId = request.isSetQueryId() ? request.getQueryId() : 
null;
-        boolean enableAdaptiveRandomBucket = 
request.isSetEnableAdaptiveRandomBucket()
-                && request.isEnableAdaptiveRandomBucket();
-        boolean loadToSingleTablet = request.isSetLoadToSingleTablet() && 
request.isLoadToSingleTablet();
-        final boolean replaceHasBeEndpoint = request.isSetBeEndpoint();
         // Lazy: resolved on the first CloudTablet that needs it.
         String replaceCachedClusterId = null;
-        for (long partitionId : resultPartitionIds) {
-            Partition partition = olapTable.getPartition(partitionId);
+        for (PartitionResultSnapshot partitionSnapshot : partitionSnapshots) {
+            Partition partition = partitionSnapshot.partition;
+            long partitionId = partitionSnapshot.partitionId;
+            TOlapTablePartition tPartition = partitionSnapshot.tPartition;
+            boolean cacheLoadTabletIdx = partitionSnapshot.cacheLoadTabletIdx;
+            partitions.add(tPartition);
             // For thread safety, we preserve the tablet distribution 
information of each partition
             // before calling getOrSetAutoPartitionInfo, but not check the 
partition first
             List<TTabletLocation> partitionTablets = new ArrayList<>();
             List<TTabletLocation> partitionSlaveTablets = new ArrayList<>();
-            TOlapTablePartition tPartition = new TOlapTablePartition();
-            tPartition.setId(partition.getId());
-
-            // set partition keys
-            int partColNum = partitionInfo.getPartitionColumns().size();
-            try {
-                OlapTableSink.setPartitionKeys(tPartition, 
partitionInfo.getItem(partition.getId()), partColNum);
-            } catch (UserException ex) {
-                errorStatus.setErrorMsgs(Lists.newArrayList(ex.getMessage()));
-                result.setStatus(errorStatus);
-                LOG.warn("send replace partition error status: {}", result);
-                return result;
-            }
-            for (MaterializedIndex index : 
partition.getMaterializedIndices(MaterializedIndex.IndexExtState.ALL)) {
-                tPartition.addToIndexes(new 
TOlapTableIndexTablets(index.getId(), Lists.newArrayList(
-                        
index.getTablets().stream().map(Tablet::getId).collect(Collectors.toList()))));
-                tPartition.setNumBuckets(index.getTablets().size());
-            }
-            
tPartition.setIsMutable(olapTable.getPartitionInfo().getIsMutable(partition.getId()));
-            boolean randomDistribution =
-                    partition.getDistributionInfo().getType() == 
DistributionInfo.DistributionInfoType.RANDOM;
-            boolean cacheLoadTabletIdx =
-                    (loadToSingleTablet || enableAdaptiveRandomBucket) && 
randomDistribution;
-            partitions.add(tPartition);
             // tablet
             AtomicLong cachedLoadTabletIdx = new AtomicLong(-1);
             if (needUseCache && txnId != 0
                     && 
Env.getCurrentGlobalTransactionMgr().getAutoPartitionCacheMgr()
-                            .getAutoPartitionInfo(txnId, partition.getId(), 
partitionTablets,
+                            .getAutoPartitionInfo(txnId, partitionId, 
partitionTablets,
                                     partitionSlaveTablets, 
cachedLoadTabletIdx)) {
                 if (cacheLoadTabletIdx) {
                     tPartition.setLoadTabletIdx(cachedLoadTabletIdx.get());
@@ -4409,53 +4394,50 @@ public class FrontendServiceImpl implements 
FrontendService.Iface {
                     return result;
                 }
             }
-            int quorum = 
olapTable.getPartitionInfo().getReplicaAllocation(partition.getId()).getTotalReplicaNum()
 / 2
-                    + 1;
-            for (MaterializedIndex index : 
partition.getMaterializedIndices(MaterializedIndex.IndexExtState.ALL)) {
-                for (Tablet tablet : index.getTablets()) {
-                    // we should ensure the replica backend is alive
-                    // otherwise, there will be a 'unknown node id, id=xxx' 
error for stream load
-                    // BE id -> path hash
-                    Multimap<Long, Long> bePathsMap;
-                    try {
-                        if (tablet instanceof CloudTablet) {
-                            CloudTablet cloudTablet = (CloudTablet) tablet;
-                            if (replaceHasBeEndpoint) {
-                                bePathsMap = 
cloudTablet.getNormalReplicaBackendPathMap(request.be_endpoint);
-                            } else {
-                                if (replaceCachedClusterId == null) {
-                                    replaceCachedClusterId = 
((CloudSystemInfoService) Env.getCurrentSystemInfo())
-                                            .getCurrentClusterId();
-                                }
-                                bePathsMap = cloudTablet
-                                        
.getNormalReplicaBackendPathMapByClusterId(replaceCachedClusterId);
-                            }
+            int quorum = partitionSnapshot.quorum;
+            for (Tablet tablet : partitionSnapshot.tablets) {
+                // we should ensure the replica backend is alive
+                // otherwise, there will be a 'unknown node id, id=xxx' error 
for stream load
+                // BE id -> path hash
+                Multimap<Long, Long> bePathsMap;
+                try {
+                    if (tablet instanceof CloudTablet) {
+                        CloudTablet cloudTablet = (CloudTablet) tablet;
+                        if (replaceHasBeEndpoint) {
+                            bePathsMap = 
cloudTablet.getNormalReplicaBackendPathMap(request.be_endpoint);
                         } else {
-                            bePathsMap = 
tablet.getNormalReplicaBackendPathMap();
+                            if (replaceCachedClusterId == null) {
+                                replaceCachedClusterId = 
((CloudSystemInfoService) Env.getCurrentSystemInfo())
+                                        .getCurrentClusterId();
+                            }
+                            bePathsMap = cloudTablet
+                                    
.getNormalReplicaBackendPathMapByClusterId(replaceCachedClusterId);
                         }
-                    } catch (UserException ex) {
-                        
errorStatus.setErrorMsgs(Lists.newArrayList(ex.getMessage()));
-                        result.setStatus(errorStatus);
-                        LOG.warn("send replace partition error status: {}", 
result);
-                        return result;
-                    }
-                    if (bePathsMap.keySet().size() < quorum) {
-                        LOG.warn("auto go quorum exception");
-                    }
-                    if (request.isSetWriteSingleReplica() && 
request.isWriteSingleReplica()) {
-                        Long[] nodes = bePathsMap.keySet().toArray(new 
Long[0]);
-                        Random random = new SecureRandom();
-                        Long masterNode = nodes[random.nextInt(nodes.length)];
-                        Multimap<Long, Long> slaveBePathsMap = bePathsMap;
-                        slaveBePathsMap.removeAll(masterNode);
-                        partitionTablets.add(new 
TTabletLocation(tablet.getId(),
-                                
Lists.newArrayList(Sets.newHashSet(masterNode))));
-                        partitionSlaveTablets.add(new 
TTabletLocation(tablet.getId(),
-                                Lists.newArrayList(slaveBePathsMap.keySet())));
                     } else {
-                        partitionTablets.add(new 
TTabletLocation(tablet.getId(),
-                                Lists.newArrayList(bePathsMap.keySet())));
+                        bePathsMap = tablet.getNormalReplicaBackendPathMap();
                     }
+                } catch (UserException ex) {
+                    
errorStatus.setErrorMsgs(Lists.newArrayList(ex.getMessage()));
+                    result.setStatus(errorStatus);
+                    LOG.warn("send replace partition error status: {}", 
result);
+                    return result;
+                }
+                if (bePathsMap.keySet().size() < quorum) {
+                    LOG.warn("auto go quorum exception");
+                }
+                if (request.isSetWriteSingleReplica() && 
request.isWriteSingleReplica()) {
+                    Long[] nodes = bePathsMap.keySet().toArray(new Long[0]);
+                    Random random = new SecureRandom();
+                    Long masterNode = nodes[random.nextInt(nodes.length)];
+                    Multimap<Long, Long> slaveBePathsMap = bePathsMap;
+                    slaveBePathsMap.removeAll(masterNode);
+                    partitionTablets.add(new TTabletLocation(tablet.getId(),
+                            Lists.newArrayList(Sets.newHashSet(masterNode))));
+                    partitionSlaveTablets.add(new 
TTabletLocation(tablet.getId(),
+                            Lists.newArrayList(slaveBePathsMap.keySet())));
+                } else {
+                    partitionTablets.add(new TTabletLocation(tablet.getId(),
+                            Lists.newArrayList(bePathsMap.keySet())));
                 }
             }
 
@@ -4487,14 +4469,14 @@ public class FrontendServiceImpl implements 
FrontendService.Iface {
             if (needUseCache) {
                 long loadTabletIdx = cacheLoadTabletIdx ? 
tPartition.getLoadTabletIdx() : -1;
                 long cachedTabletIdx = 
Env.getCurrentGlobalTransactionMgr().getAutoPartitionCacheMgr()
-                        .getOrSetAutoPartitionInfo(txnId, partition.getId(), 
partitionTablets,
+                        .getOrSetAutoPartitionInfo(txnId, partitionId, 
partitionTablets,
                                 partitionSlaveTablets, loadTabletIdx);
                 if (cacheLoadTabletIdx) {
                     tPartition.setLoadTabletIdx(cachedTabletIdx);
                 }
                 if (LOG.isDebugEnabled()) {
                     LOG.debug("Cache auto partition info, txnId: {}, 
partitionId: {}, "
-                            + "tablets: {}, slaveTablets: {}", txnId, 
partition.getId(),
+                            + "tablets: {}, slaveTablets: {}", txnId, 
partitionId,
                             partitionTablets.size(), 
partitionSlaveTablets.size());
                 }
             }
@@ -4526,6 +4508,94 @@ public class FrontendServiceImpl implements 
FrontendService.Iface {
         return result;
     }
 
+    private static final class PartitionResultSnapshot {
+        private final Partition partition;
+        private final long partitionId;
+        private final TOlapTablePartition tPartition;
+        private final List<Tablet> tablets;
+        private final int quorum;
+        private final boolean cacheLoadTabletIdx;
+
+        private PartitionResultSnapshot(Partition partition, long partitionId,
+                TOlapTablePartition tPartition, List<Tablet> tablets, int 
quorum, boolean cacheLoadTabletIdx) {
+            this.partition = partition;
+            this.partitionId = partitionId;
+            this.tPartition = tPartition;
+            this.tablets = tablets;
+            this.quorum = quorum;
+            this.cacheLoadTabletIdx = cacheLoadTabletIdx;
+        }
+    }
+
+    private static List<PartitionResultSnapshot> 
snapshotPartitionResultsByName(OlapTable olapTable,
+            Collection<String> partitionNames, boolean loadToSingleTablet, 
boolean enableAdaptiveRandomBucket,
+            String resultName) throws UserException {
+        PartitionInfo partitionInfo = olapTable.getPartitionInfo();
+        int partColNum = partitionInfo.getPartitionColumns().size();
+        List<PartitionResultSnapshot> partitionSnapshots = new ArrayList<>();
+        for (String partitionName : partitionNames) {
+            Partition partition = olapTable.getPartition(partitionName);
+            if (partition == null) {
+                throw new UserException(String.format(
+                        "partition %s was dropped concurrently while building 
%s result, please retry",
+                        partitionName, resultName));
+            }
+            partitionSnapshots.add(snapshotPartitionResult(partitionInfo, 
partColNum, partition, partitionName,
+                    loadToSingleTablet, enableAdaptiveRandomBucket, 
resultName));
+        }
+        return partitionSnapshots;
+    }
+
+    private static List<PartitionResultSnapshot> 
snapshotPartitionResultsById(OlapTable olapTable,
+            Collection<Long> partitionIds, boolean loadToSingleTablet, boolean 
enableAdaptiveRandomBucket,
+            String resultName) throws UserException {
+        PartitionInfo partitionInfo = olapTable.getPartitionInfo();
+        int partColNum = partitionInfo.getPartitionColumns().size();
+        List<PartitionResultSnapshot> partitionSnapshots = new ArrayList<>();
+        for (long partitionId : partitionIds) {
+            Partition partition = olapTable.getPartition(partitionId);
+            if (partition == null) {
+                throw new UserException(String.format(
+                        "partition %d was dropped concurrently while building 
%s result, please retry",
+                        partitionId, resultName));
+            }
+            partitionSnapshots.add(snapshotPartitionResult(partitionInfo, 
partColNum, partition,
+                    String.valueOf(partitionId), loadToSingleTablet, 
enableAdaptiveRandomBucket, resultName));
+        }
+        return partitionSnapshots;
+    }
+
+    private static PartitionResultSnapshot 
snapshotPartitionResult(PartitionInfo partitionInfo, int partColNum,
+            Partition partition, String partitionLabel, boolean 
loadToSingleTablet,
+            boolean enableAdaptiveRandomBucket, String resultName) throws 
UserException {
+        long partitionId = partition.getId();
+        PartitionItem partitionItem = partitionInfo.getItem(partitionId);
+        if (partitionItem == null) {
+            throw new UserException(String.format(
+                    "partition item of %s was dropped concurrently while 
building %s result, please retry",
+                    partitionLabel, resultName));
+        }
+
+        TOlapTablePartition tPartition = new TOlapTablePartition();
+        tPartition.setId(partitionId);
+        OlapTableSink.setPartitionKeys(tPartition, partitionItem, partColNum);
+        List<Tablet> partitionTabletSnapshot = new ArrayList<>();
+        for (MaterializedIndex index : 
partition.getMaterializedIndices(MaterializedIndex.IndexExtState.ALL)) {
+            List<Tablet> indexTablets = new ArrayList<>(index.getTablets());
+            tPartition.addToIndexes(new TOlapTableIndexTablets(index.getId(), 
Lists.newArrayList(
+                    
indexTablets.stream().map(Tablet::getId).collect(Collectors.toList()))));
+            tPartition.setNumBuckets(indexTablets.size());
+            partitionTabletSnapshot.addAll(indexTablets);
+        }
+        tPartition.setIsMutable(partitionInfo.getIsMutable(partitionId));
+        boolean randomDistribution =
+                partition.getDistributionInfo().getType() == 
DistributionInfo.DistributionInfoType.RANDOM;
+        boolean cacheLoadTabletIdx = (loadToSingleTablet || 
enableAdaptiveRandomBucket) && randomDistribution;
+        int quorum = 
partitionInfo.getReplicaAllocation(partitionId).getTotalReplicaNum() / 2 + 1;
+        return new PartitionResultSnapshot(partition, partitionId, tPartition, 
partitionTabletSnapshot, quorum,
+                cacheLoadTabletIdx);
+    }
+
     public TGetMetaResult getMeta(TGetMetaRequest request) throws TException {
         String clientAddr = getClientAddrAsString();
         if (LOG.isDebugEnabled()) {
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/service/FrontendServiceImplTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/service/FrontendServiceImplTest.java
index 36eb1bcf28e..bc2bb33545c 100644
--- 
a/fe/fe-core/src/test/java/org/apache/doris/service/FrontendServiceImplTest.java
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/service/FrontendServiceImplTest.java
@@ -89,6 +89,7 @@ import java.util.HashMap;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.UUID;
+import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.stream.Collectors;
 
 public class FrontendServiceImplTest {
@@ -202,6 +203,54 @@ public class FrontendServiceImplTest {
         Assert.assertNotNull(p20230807);
     }
 
+    @Test
+    public void 
testCreatePartitionReturnsRetryErrorWhenResultPartitionIsMissing() throws 
Exception {
+        String createOlapTblStmt = "CREATE TABLE 
test.partition_dropped_before_result_snapshot(\n"
+                + "    event_day DATETIME NOT NULL,\n"
+                + "    site_id INT\n"
+                + ")\n"
+                + "DUPLICATE KEY(event_day, site_id)\n"
+                + "AUTO PARTITION BY RANGE (date_trunc(event_day, 'day')) ()\n"
+                + "DISTRIBUTED BY HASH(event_day) BUCKETS 1\n"
+                + "PROPERTIES(\"replication_num\" = \"1\");";
+        createTable(createOlapTblStmt);
+
+        Database db = 
Env.getCurrentInternalCatalog().getDbOrAnalysisException("test");
+        OlapTable table = (OlapTable) 
db.getTableOrAnalysisException("partition_dropped_before_result_snapshot");
+        OlapTable spyTable = Mockito.spy(table);
+        String partitionName = "p20230808000000";
+        AtomicBoolean hideResultPartition = new AtomicBoolean(true);
+        Mockito.doAnswer(invocation -> {
+            Partition partition = (Partition) invocation.callRealMethod();
+            // Model the lookup result after a concurrent retention drop 
without timing-dependent test threads.
+            if (partition != null && hideResultPartition.compareAndSet(true, 
false)) {
+                return null;
+            }
+            return partition;
+        }).when(spyTable).getPartition(partitionName);
+
+        db.unregisterTable(table.getName());
+        db.registerTable(spyTable);
+        try {
+            TNullableStringLiteral start = new TNullableStringLiteral();
+            start.setValue("2023-08-08 00:00:00");
+            TCreatePartitionRequest request = new TCreatePartitionRequest();
+            request.setDbId(db.getId());
+            request.setTableId(spyTable.getId());
+            
request.setPartitionValues(Collections.singletonList(Collections.singletonList(start)));
+
+            TCreatePartitionResult result = new 
FrontendServiceImpl(exeEnv).createPartition(request);
+
+            Assert.assertEquals(TStatusCode.RUNTIME_ERROR, 
result.getStatus().getStatusCode());
+            Assert.assertTrue(result.getStatus().getErrorMsgs().get(0)
+                    .contains("was dropped concurrently while building auto 
partition result, please retry"));
+            Assert.assertFalse(hideResultPartition.get());
+        } finally {
+            db.unregisterTable(spyTable.getName());
+            db.registerTable(table);
+        }
+    }
+
     @Test
     public void testCreatePartitionList() throws Exception {
         String createOlapTblStmt = new String("CREATE TABLE 
test.partition_list(\n"


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


Reply via email to