CRZbulabula opened a new pull request, #17934:
URL: https://github.com/apache/iotdb/pull/17934

   ## Problem
   
   When a `remove datanode` is in progress, the ConfigNode could still allocate 
brand-new Region replicas onto the DataNode being removed.
   
   This is especially likely when the target DataNode was killed (e.g. `kill 
-9`) before the removal: the failure detector reports such a node as `Unknown` 
rather than `Removing`, and `RegionBalancer` **intentionally** keeps `Unknown` 
DataNodes as allocation candidates (to cope with an insufficient number of 
online nodes). As a result, a new region (e.g. for the internal `root.__system` 
database) could be assigned a replica on the dead node. That replica can never 
be created there (`Connection refused`), yet the metadata keeps the assignment 
and retries forever, so the `RemoveDataNodesProcedure` gets stuck and the 
target DataNode never disappears from `show datanodes`.
   
   Observed timeline (from the report):
   ```
   11:31:06.210  Submit RemoveDataNodesProcedure successfully
   11:31:06.550  create SchemaRegion 8 on DataNodes [3, 7, 6]   # DN7 is being 
removed
   11:31:07.444  create DataRegion 9 on DataNodes [5, 7]        # DN7 is being 
removed
                 Failed to CREATE_*_REGION on DataNode 7: Connection refused  
(retried forever)
   ```
   
   ## Root cause
   
   `RegionBalancer.genRegionGroupsAllocationPlan` gathers allocation candidates 
with:
   
   ```java
   getNodeManager().filterDataNodeThroughStatus(NodeStatus.Running, 
NodeStatus.Unknown)
   ```
   
   A node-status filter alone cannot fix this: a DataNode killed before removal 
is `Unknown` (not `Removing`), because 
`DataNodeHeartbeatCache.updateCurrentStatistics` lets the failure detector 
override the `Removing` status back to `Unknown` once heartbeats stop. So the 
removing node still passes the filter.
   
   ## Fix
   
   Consult the in-progress `RemoveDataNodesProcedure` — the authoritative, 
leader-switch-durable source of which DataNodes are being removed — and exclude 
those nodes from the allocation candidates.
   
   - Add `ProcedureManager.getRemovingDataNodeIds()`, which scans the 
unfinished `RemoveDataNodesProcedure`(s) and returns the removing DataNode ids. 
This mirrors the existing pattern in `ProcedureManager.checkRemoveDataNodes` / 
`checkRegionOperationWithRemoveDataNode`.
   - `RegionBalancer.genRegionGroupsAllocationPlan` now filters those ids out 
of the candidate list, in the same spirit as the existing 
`RemoveDataNodeHandler.selectedRegionMigrationPlans`.
   
   This keeps the existing "allow `Unknown` candidates when online nodes are 
scarce" behavior intact, and only removes nodes that are actively being 
removed. The removal procedure already guarantees 
(`checkEnoughDataNodeAfterRemoving`) that enough non-removing nodes remain, so 
this never spuriously triggers `NotEnoughDataNodeException`.
   
   ## Test
   
   `IoTDBRemoveDataNodeRegionAllocationIT` kills a DataNode that hosts regions, 
submits the removal, and — while the removal is still in progress — forces a 
fresh Region allocation by creating a new database. It asserts that none of the 
**newly allocated** regions land on the DataNode being removed (comparing 
against a pre-allocation snapshot of region ids, since the removing node 
legitimately keeps hosting its own pre-existing regions until each finishes 
migrating away), and that the removal then completes.
   
   🤖 Generated with [Claude Code](https://claude.com/claude-code)
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to