[ 
https://issues.apache.org/jira/browse/HDDS-17229?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Siyao Meng updated HDDS-17229:
------------------------------
    Attachment: TestBugCR10StaleRestoreTopology.java

> A heartbeat that arrives while SCM marks a stale datanode DEAD leaves it 
> HEALTHY but missing from the network topology
> ----------------------------------------------------------------------------------------------------------------------
>
>                 Key: HDDS-17229
>                 URL: https://issues.apache.org/jira/browse/HDDS-17229
>             Project: Apache Ozone
>          Issue Type: Bug
>            Reporter: Siyao Meng
>            Priority: Major
>         Attachments: CR-10-skip-stale-restore-after-node-marked-dead.patch, 
> TestBugCR10StaleRestoreTopology.java
>
>
> h3. Mechanism
> In {{NodeStateManager.checkNodesHealth}} the STALE case calls 
> {{updateNodeState}} twice with the same STALE {{NodeStatus}} snapshot: first 
> with the dead condition and TIMEOUT, then with the healthy condition and 
> RESTORE. Each call reads the last heartbeat time again. If the first call 
> marks the node DEAD, {{updateClusterMap}} removes it from the 
> {{NetworkTopology}}. If a heartbeat is recorded before the second call 
> ({{SCMNodeManager.processHeartbeat}} calls 
> {{NodeStateManager.updateLastHeartbeatTime}} on an RPC handler thread, which 
> does not coordinate with the health check), the second call moves the node to 
> HEALTHY. It passes the STALE snapshot as the old status, and 
> {{updateClusterMap}} only adds a node back when the old status is DEAD, so 
> the node is not added back.
> Nothing adds it later. Apart from recovery from DEAD, the topology is only 
> added to on first registration and on re-registration with a changed address, 
> ports or version. Further heartbeats and health checks leave it out until the 
> node dies and recovers again or SCM restarts.
> This came with HDDS-16623, which moved topology updates from the event 
> handlers into {{updateNodeState}}. Before that, 
> {{HealthyReadOnlyNodeHandler}}, which handled the event fired by RESTORE 
> (then a STALE to HEALTHY_READONLY transition), added the node to the topology 
> unconditionally, which covered this case. The defect exists only since 
> HDDS-16623 (fix version 2.3.0, not yet released), so no released version is 
> affected.
> h3. Trigger
> # A datanode stops heartbeating, goes STALE, and its last heartbeat becomes 
> older than the dead interval.
> # The health check marks it DEAD and removes it from the topology.
> # A heartbeat from the datanode is recorded before the same health check 
> reaches its RESTORE check.
> No fault injection is needed, but the window is narrow: it lies between two 
> consecutive calls in one iteration of the health check loop (a node state map 
> update, a topology removal under the topology write lock and an event 
> dispatch), and only for a node whose heartbeat returns right at the dead 
> deadline.
> h3. Impact
> The datanode is HEALTHY in SCM but absent from {{NetworkTopology}}: in the 
> reproduction {{getNumOfLeafNode}} returns 0 and {{chooseRandom}} returns null 
> with the node HEALTHY. Placement that picks nodes from the topology (for 
> example {{SCMContainerPlacementRackAware}} and 
> {{SCMContainerPlacementRackScatter}}) cannot choose it (argued from code, 
> placement itself not run). The state is silent and persists until the node 
> goes DEAD and recovers again, re-registers with changed details, or SCM 
> restarts.
> h3. Reproduction
> PASS, deterministic, unmodified source. 
> [^TestBugCR10StaleRestoreTopology.java] ({{hadoop-hdds/server-scm}}) uses the 
> real {{NodeStateManager}} and {{NetworkTopologyImpl}} with default 
> configuration and the periodic health check paused. It sets the last 
> heartbeat time back past the stale and then the dead interval, and delivers a 
> heartbeat through {{NodeStateManager.updateLastHeartbeatTime}} on a separate 
> thread while the health check thread is held in its DEAD_NODE event publish. 
> Afterwards the node is HEALTHY, missing from the topology, the events are 
> DEAD_NODE then UNHEALTHY_TO_HEALTHY_NODE, and five more heartbeats and health 
> checks do not change that. A passing test means the defect is present.
> h3. Patch
> [^CR-10-skip-stale-restore-after-node-marked-dead.patch], against 
> b0aa6475b7856d2c7f467dd3db12b9a1dd116f63. It also applies to master at 
> 20d95ed37fe.
> In the STALE case the RESTORE check now runs only if the TIMEOUT check did 
> not change the node ({{updateNodeState}} returns whether it applied a 
> transition). For this interleaving the node stays DEAD until the next health 
> check (3s by default), so it is handled like a node whose heartbeat arrives 
> just after the health check: {{DeadNodeHandler}}, which skips a node that is 
> no longer DEAD when it runs, now processes it as DEAD (closes its containers, 
> destroys its pipelines, removes its replicas and notifies 
> ReplicationManager), and the next check moves it to HEALTHY through the DEAD 
> case and adds it back to the topology. This is the intended outcome, as the 
> node was silent past the dead interval. Before the patch the RESTORE call ran 
> microseconds after DEAD_NODE was fired, so the handler usually found the node 
> HEALTHY and skipped it.
> Covered by 
> {{testHeartbeatWhileMarkedDeadDoesNotLeaveHealthyNodeOutOfTopology}} in the 
> existing {{TestNodeStateManager}}, which delivers the heartbeat from the 
> DEAD_NODE publish. Without the change it fails because the node is not in the 
> topology. With it, {{TestNodeStateManager}}, {{TestSCMNodeManager}}, 
> {{TestDeadNodeHandler}}, {{TestNodeDecommissionManager}}, 
> {{TestSCMNodeMetrics}}, {{TestNodeReportHandler}} and both 
> {{TestContainerPlacement}} classes pass (110 tests), and checkstyle is clean. 
> With the patch the reproduction fails because the node is DEAD after the 
> racing health check; the regression test shows that the next check adds it 
> back. HDDS-16633 has the same symptom from a different trigger 
> (re-registration) and is not changed by this patch.
> Found by code review of the SCM datanode decommission and maintenance admin 
> paths, as part of the TLA+ verification effort under HDDS-15926, on commit 
> b0aa6475b7856d2c7f467dd3db12b9a1dd116f63. Checked against HDDS issues and 
> apache/ozone pull requests for duplicates before filing. The attached patch 
> is a proposal for review. Generated with Specula (Claude Opus 5.5).



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

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

Reply via email to