You are viewing a plain text version of this content. The canonical link for it is here.
Posted to issues@ozone.apache.org by "Siyao Meng (Jira)" <ji...@apache.org> on 2026/10/11 18:53:00 UTC
[jira] [Updated] (HDDS-16906) Pipeline with a DEAD datanode stays OPEN if SCM leadership changes while the node goes STALE and DEAD, and its EC container is not repaired
[ https://issues.apache.org/jira/browse/HDDS-16906?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]
Siyao Meng updated HDDS-16906:
------------------------------
Attachment: TestBugMC7DeadEcPipelineMemberAfterFailovers.java
> Pipeline with a DEAD datanode stays OPEN if SCM leadership changes while the node goes STALE and DEAD, and its EC container is not repaired
> -------------------------------------------------------------------------------------------------------------------------------------------
>
> Key: HDDS-16906
> URL: https://issues.apache.org/jira/browse/HDDS-16906
> Project: Apache Ozone
> Issue Type: Bug
> Reporter: Siyao Meng
> Priority: Minor
> Attachments: MC-7-scrub-open-pipelines-with-dead-nodes.patch, TestBugMC7DeadEcPipelineMemberAfterFailovers.java
>
>
> h3. Mechanism
> Every SCM, leader or follower, runs its own node health check and fires {{STALE_NODE}} and {{DEAD_NODE}} once per transition. {{StaleNodeHandler}} and {{DeadNodeHandler}} close the node's pipelines through {{PipelineManager.closePipeline}}, which is a Ratis request: on a follower it fails with NotLeaderException. {{StaleNodeHandler}} logs "Could not finalize pipeline" at INFO without the exception, {{DeadNodeHandler}} logs the exception, and both drop it. No SCM fires the events again for a node that is already DEAD, and nothing re-runs the handlers when an SCM becomes leader. The only periodic check, {{PipelineManagerImpl.scrubPipelines}} on the leader, closes an OPEN pipeline only if a member is not registered (HDDS-7341); a DEAD datanode stays registered.
> So if the SCM that becomes leader saw the STALE transition while it was a follower, and the same happens for the DEAD transition, no SCM closes the pipeline. For RATIS/THREE pipelines the remaining datanodes can still trigger the close through pipeline actions (argued, not run). A RATIS/ONE pipeline on the dead datanode and EC pipelines have no such path, because no other member is left or because EC datanodes are not aware of pipelines.
> h3. Trigger
> # An OPEN EC pipeline, three SCMs, and a datanode of the pipeline stops.
> # The datanode goes STALE. A follower sees it first, its handler fails, and that follower becomes leader before the old leader's close has been applied.
> # The datanode goes DEAD. Again the SCM that becomes leader saw it first as a follower.
> Each SCM checks node health on its own timer ({{ozone.scm.heartbeat.thread.interval}}, default 3s), so each leader change has to fall in a window of a few seconds around the transition, at both transitions (5 minutes apart with the default stale and dead intervals). A close that fails on the leader for another reason is lost the same way (argued, not run).
> h3. Impact
> * The EC pipeline stays OPEN with a DEAD member. {{SCMBlockProtocolServer.allocateBlock}} keeps returning it to requests that do not exclude it. The client then fails over: it adds the pipeline to its exclude list and asks for another one (argued from the client code, not run).
> * Its OPEN container keeps 4 of 5 replicas. {{ReplicationManager}} does not reconstruct OPEN containers, and the container cannot fill, so it stays under replicated until the datanode returns, an admin closes the pipeline or container, or an SCM that was restarted after the node died becomes leader (then the node is unregistered there and the existing scrubber rule closes the pipeline).
> * In the same run, two RATIS/THREE pipelines and one RATIS/ONE pipeline with the dead datanode were also still OPEN on the new leader after a scrubber pass. Whether the datanodes close the RATIS/THREE ones later was not observed.
> h3. Reproduction
> PASS, unmodified source. [^TestBugMC7DeadEcPipelineMemberAfterFailovers.java] ({{hadoop-ozone/integration-test}}) starts 3 SCMs, 1 OM and 5 datanodes with the stale and dead intervals shortened to 6s and 40s, and writes one EC 3-2 key with the client. It widens the two windows with the existing {{SCMNodeManager.pauseHealthCheck}} test hook on the current leader and moves leadership with the admin {{transferLeadership}} RPC: followers B and C see STALE, leadership moves A to B, followers A and C see DEAD, leadership moves B to C. On C it then runs one scrubber pass and one ReplicationManager pass. Output on the pin: the EC pipeline is OPEN with a DEAD member, its container is OPEN with 4 replicas, {{allocateBlock}} for EC 3-2 returns that pipeline, and the log shows the stale node handler's "Could not finalize pipeline" lines and the dead node handler failing with NotLeaderException on the followers. As a control, a second member stopped while C is leader gets the pipeline closed. A passing test means the defect is present. With the patch it fails at the first check, because the scrubber pass closes the pipeline and the container moves to CLOSING.
> h3. Patch
> [^MC-7-scrub-open-pipelines-with-dead-nodes.patch], against b0aa6475b7856d2c7f467dd3db12b9a1dd116f63. It also applies to master at b9b33851b20.
> {{scrubPipelines}} also closes an OPEN pipeline that has a DEAD member, next to the existing rule for unregistered members. The scrubber runs only on the leader, so the close no longer depends on which SCM saw the transition, and it also covers a close that failed on a stable leader. {{closePipeline}} closes the pipeline's containers too, so ReplicationManager can repair them once they are closed. The scrubber runs only on a leader that is ready and out of safe mode. In normal operation the leader's stale or dead node handler has already closed every pipeline of a DEAD node, of any type, so the rule finds nothing. If such a close was lost, the scrubber does it at most one scrub interval (default 5 minutes) after the node is DEAD. It does not act on STALE members. Covered by {{testScrubOpenWithDeadNodes}} in the existing {{TestPipelineManagerImpl}} (a STALE member keeps the pipeline open, a DEAD member closes it), which fails without the change. With it the SCM pipeline unit suites, {{TestSCMSafeModeManager}}, {{TestSCMNodeManager}} and {{TestDeadNodeHandler}} (359 tests) and {{TestDecommissionAndMaintenance}} (9 tests) pass, checkstyle is clean.
> Related: HDDS-16887. If {{DeadNodeHandler}} deletes the pipeline during a scrubber pass, the new close hits the {{PipelineNotFoundException}} that HDDS-16887 handles (today it ends the pass, as it already can for the existing rules). HDDS-16887 also wraps the loop body in {{scrubPipelines}} in a {{try}} block, so the patches conflict textually and whichever lands second needs a trivial rebase.
> Found by TLA+ model checking and code review of the SCM pipeline lifecycle paths 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: issues-unsubscribe@ozone.apache.org
For additional commands, e-mail: issues-help@ozone.apache.org