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

CRZbulabula pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/iotdb.git


The following commit(s) were added to refs/heads/master by this push:
     new 87a6d198a90 Allow removing regions from read-only DataNodes (#18670)
87a6d198a90 is described below

commit 87a6d198a901f46630a3455f59b264fc9368ca08
Author: Yongzao <[email protected]>
AuthorDate: Fri Sep 18 13:57:00 2026 +0800

    Allow removing regions from read-only DataNodes (#18670)
---
 .../iotdb/confignode/manager/ProcedureManager.java | 28 +++++++++++++++-------
 .../ProcedureManagerReconstructRegionTest.java     | 23 +++++++++++++++++-
 2 files changed, 41 insertions(+), 10 deletions(-)

diff --git 
a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/ProcedureManager.java
 
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/ProcedureManager.java
index ac1a53539f6..28b54fa61d5 100644
--- 
a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/ProcedureManager.java
+++ 
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/ProcedureManager.java
@@ -861,7 +861,8 @@ public class ProcedureManager {
                     new Pair<>("Original DataNode", originalDataNode),
                     new Pair<>("Destination DataNode", destDataNode),
                     new Pair<>("Coordinator for add peer", 
coordinatorForAddPeer)),
-                migrateRegionReq.getModel()))
+                migrateRegionReq.getModel(),
+                NodeStatus.Running))
         != null) {
       // do nothing
     } else if (configManager
@@ -905,7 +906,8 @@ public class ProcedureManager {
             Arrays.asList(
                 new Pair<>("Target DataNode", targetDataNode),
                 new Pair<>("Coordinator", coordinator)),
-            req.getModel());
+            req.getModel(),
+            NodeStatus.Running);
 
     if (configManager
             .getPartitionManager()
@@ -946,7 +948,8 @@ public class ProcedureManager {
             Arrays.asList(
                 new Pair<>("Target DataNode", targetDataNode),
                 new Pair<>("Coordinator", coordinator)),
-            req.getModel());
+            req.getModel(),
+            NodeStatus.Running);
     if (configManager
         .getPartitionManager()
         .getAllReplicaSets(targetDataNode.getDataNodeId())
@@ -977,7 +980,9 @@ public class ProcedureManager {
             regionId,
             targetDataNode,
             Arrays.asList(new Pair<>("Coordinator", coordinator)),
-            req.getModel());
+            req.getModel(),
+            NodeStatus.Running,
+            NodeStatus.ReadOnly);
 
     if (configManager
             .getPartitionManager()
@@ -1011,15 +1016,17 @@ public class ProcedureManager {
    * removing
    *
    * @param regionId region group id, also called consensus group id
-   * @param targetDataNode DataNode should in Running status
+   * @param targetDataNode DataNode participating in the region operation
    * @param relatedDataNodes Pair<Identity, Node Location>
+   * @param targetDataNodeAllowedStatuses statuses accepted for the target 
DataNode
    * @return The reason if check failed, or null if check pass
    */
   private String regionOperationCommonCheck(
       TConsensusGroupId regionId,
       TDataNodeLocation targetDataNode,
       List<Pair<String, TDataNodeLocation>> relatedDataNodes,
-      Model model) {
+      Model model,
+      NodeStatus... targetDataNodeAllowedStatuses) {
     String failMessage;
     ConfigNodeConfig conf = ConfigNodeDescriptor.getInstance().getConf();
 
@@ -1036,13 +1043,16 @@ public class ProcedureManager {
           relatedDataNodes.stream().filter(pair -> pair.getRight() == 
null).findAny().get();
       failMessage = String.format("Cannot find %s", nullPair.getLeft());
     } else if (targetDataNode != null
-        && 
!configManager.getNodeManager().filterDataNodeThroughStatus(NodeStatus.Running).stream()
+        && !configManager
+            .getNodeManager()
+            .filterDataNodeThroughStatus(targetDataNodeAllowedStatuses)
+            .stream()
             .map(TDataNodeConfiguration::getLocation)
             .map(TDataNodeLocation::getDataNodeId)
             .collect(Collectors.toSet())
             .contains(targetDataNode.getDataNodeId())) {
-      // Here we only check Running DataNode to implement migration, because 
removing nodes may not
-      // exist when add peer is performing
+      // The accepted statuses depend on the region operation. For example, 
REMOVE REGION also
+      // accepts a ReadOnly target because the target replica is being removed.
       failMessage =
           String.format(
               "Target DataNode %s is not in Running status.", 
targetDataNode.getDataNodeId());
diff --git 
a/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/manager/ProcedureManagerReconstructRegionTest.java
 
b/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/manager/ProcedureManagerReconstructRegionTest.java
index a21a5da7083..4e2b58daa63 100644
--- 
a/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/manager/ProcedureManagerReconstructRegionTest.java
+++ 
b/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/manager/ProcedureManagerReconstructRegionTest.java
@@ -36,6 +36,7 @@ import 
org.apache.iotdb.confignode.procedure.env.ConfigNodeProcedureEnv;
 import org.apache.iotdb.confignode.procedure.env.RegionMaintainHandler;
 import 
org.apache.iotdb.confignode.procedure.impl.region.ReconstructRegionProcedure;
 import org.apache.iotdb.confignode.rpc.thrift.TReconstructRegionReq;
+import org.apache.iotdb.confignode.rpc.thrift.TRemoveRegionReq;
 import org.apache.iotdb.rpc.TSStatusCode;
 
 import org.junit.Before;
@@ -71,6 +72,7 @@ public class ProcedureManagerReconstructRegionTest {
 
   private ProcedureManager manager;
   private ProcedureExecutor<ConfigNodeProcedureEnv> executor;
+  private NodeManager nodeManager;
   private PartitionManager partitionManager;
   private final ConcurrentHashMap<Long, Procedure<ConfigNodeProcedureEnv>> 
procedures =
       new ConcurrentHashMap<>();
@@ -78,7 +80,7 @@ public class ProcedureManagerReconstructRegionTest {
   @Before
   public void setUp() throws Exception {
     ConfigManager configManager = mock(ConfigManager.class);
-    NodeManager nodeManager = mock(NodeManager.class);
+    nodeManager = mock(NodeManager.class);
     partitionManager = mock(PartitionManager.class);
     ConfigNodeProcedureEnv env = mock(ConfigNodeProcedureEnv.class);
     RegionMaintainHandler handler = mock(RegionMaintainHandler.class);
@@ -90,10 +92,16 @@ public class ProcedureManagerReconstructRegionTest {
         .thenReturn(new TDataNodeConfiguration().setLocation(target));
     when(nodeManager.filterDataNodeThroughStatus(NodeStatus.Running))
         .thenReturn(Collections.singletonList(new 
TDataNodeConfiguration().setLocation(target)));
+    when(nodeManager.filterDataNodeThroughStatus(NodeStatus.Running, 
NodeStatus.ReadOnly))
+        .thenReturn(Collections.singletonList(new 
TDataNodeConfiguration().setLocation(target)));
     
when(partitionManager.findTConsensusGroupIdByRegionId(12)).thenReturn(Optional.of(firstRegion));
     when(partitionManager.findTConsensusGroupIdByRegionId(14))
         .thenReturn(Optional.of(secondRegion));
     
when(partitionManager.findTConsensusGroupIdByRegionId(99)).thenReturn(Optional.empty());
+    when(partitionManager.generateTConsensusGroupIdByRegionId(12))
+        .thenReturn(Optional.of(firstRegion));
+    when(partitionManager.generateTConsensusGroupIdByRegionId(14))
+        .thenReturn(Optional.of(secondRegion));
     
when(partitionManager.getRegionDatabase(any(TConsensusGroupId.class))).thenReturn("root.sg");
 
     Map<TConsensusGroupId, TRegionReplicaSet> replicaSets = new HashMap<>();
@@ -166,4 +174,17 @@ public class ProcedureManagerReconstructRegionTest {
     assertTrue(status.getMessage().contains("in progress"));
     verify(executor, times(0)).submitProcedure(any());
   }
+
+  @Test
+  public void testRemoveRegionAllowsReadOnlyTargetDataNode() {
+    procedures.clear();
+    when(nodeManager.filterDataNodeThroughStatus(NodeStatus.Running))
+        .thenReturn(
+            Collections.singletonList(new 
TDataNodeConfiguration().setLocation(coordinator)));
+    TRemoveRegionReq request = new 
TRemoveRegionReq(Collections.singletonList(12), 7, Model.TREE);
+
+    assertEquals(
+        TSStatusCode.SUCCESS_STATUS.getStatusCode(), 
manager.removeRegions(request).getCode());
+    verify(executor, times(1)).submitProcedure(any());
+  }
 }

Reply via email to