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

Siyao Meng updated HDDS-17064:
------------------------------
    Attachment: TestBugMC2LostFcrNotResent.java

> Full container report carried by a failed heartbeat is not resent until the 
> next periodic report, hiding volume failure losses for up to two hours
> --------------------------------------------------------------------------------------------------------------------------------------------------
>
>                 Key: HDDS-17064
>                 URL: https://issues.apache.org/jira/browse/HDDS-17064
>             Project: Apache Ozone
>          Issue Type: Bug
>            Reporter: Siyao Meng
>            Priority: Major
>         Attachments: 
> MC-2-resend-container-report-after-failed-heartbeat.patch, 
> TestBugMC2LostFcrNotResent.java
>
>
> h3. Mechanism
> {{StateContext.getFullReports}} clears the per endpoint "ready to send" flag 
> of the container report as soon as it puts the report into a heartbeat 
> request (HDDS-5111). If that heartbeat fails with an {{IOException}}, 
> {{HeartbeatEndpointTask.putBackIncrementalReports}} puts back only 
> incremental and command status reports; its comment says the full reports are 
> kept because only the latest is needed, but nothing sets the flag again. The 
> container report is therefore not sent until the next 
> {{StateContext.refreshFullReport}}, which happens only at the next periodic 
> container report or the next volume failure.
> Since HDDS-8448, {{ContainerSet.handleVolumeFailures}} removes the containers 
> of a failed volume without sending ICRs and relies on that full container 
> report (FCR) to tell SCM and Recon about the loss. If the one heartbeat 
> carrying it fails, the loss is not reported.
> h3. Trigger
> # A volume fails its health check; {{ContainerSet.handleVolumeFailures}} 
> removes its containers, refreshes the FCR and triggers a heartbeat.
> # That heartbeat fails while SCM stays up, for example an RPC timeout 
> ({{ozone.scm.heartbeat.rpc-timeout}}, 5s by default) or a reset of an open 
> connection. A refused connection is first retried by the RPC client (15 
> times, 1s apart, by default), and after an SCM restart the datanode 
> re-registers and sends a fresh report.
> # Later heartbeats succeed, without the FCR.
> The same happens to a periodic FCR, delaying the receiver's reconciliation by 
> one more period.
> h3. Impact
> * SCM keeps the replicas of every container on the failed volume until the 
> next periodic FCR from that datanode, up to one report period later 
> ({{hdds.container.report.interval}} plus a random delay of up to the same 
> interval, chosen when the datanode starts, so a fixed period between 60 and 
> 120 minutes by default). In that time ReplicationManager does not see these 
> containers as under replicated, so their re-replication is delayed by up to 
> that long (argued from the code, the SCM side was not run). SCM only asks a 
> datanode to re-register when it is not registered (as after an SCM restart, 
> which therefore does not hit this), so a reachable datanode is not asked to 
> send a fresh report. In SCM HA each SCM has its own flag, so only the SCMs 
> whose heartbeat failed miss the report, and ReplicationManager is affected 
> when the leader is one of them.
> * Recon shows the same lost replicas in its replica and container health 
> views for the same time (run). Recon asks a datanode to re-register when its 
> heartbeats stop for 3 heartbeat intervals (180s by default), so at Recon only 
> failures shorter than that are affected.
> h3. Reproduction
> PASS, deterministic, unmodified source. [^TestBugMC2LostFcrNotResent.java] 
> ({{hadoop-ozone/recon}}) runs the real datanode {{StateContext}}, 
> {{ContainerSet.handleVolumeFailures}} and {{HeartbeatEndpointTask}} against a 
> stub RPC proxy that hands each heartbeat's FCR to a real 
> {{ReconContainerReportHandler}} over a real {{ReconContainerManager}} and 
> {{SCMNodeManager}}. The volume is a mock whose {{isFailed}} is switched to 
> true, and the stub fails one heartbeat with a {{ConnectException}}, as the 
> RPC client throws once its connection retries are used up. After the failed 
> heartbeat and 10 successful ones, Recon has received only the first FCR and 
> still lists the replica the datanode removed; a control run without the 
> failed heartbeat removes it at once. Heartbeats do not go through 
> {{ReconNodeManager.processHeartbeat}}, so the 180s re-register check was 
> read, not run. A passing test means the defect is present.
> h3. Patch
> [^MC-2-resend-container-report-after-failed-heartbeat.patch], against 
> 916aa1d9949cbb10f2750a40c8704340893131a2. It also applies to master at 
> 62d982ff2d9.
> A new {{StateContext.putBackFullContainerReport}} sets the container report 
> flag of the endpoint again, and {{putBackIncrementalReports}} calls it when 
> the failed request carried a container report. The next heartbeat then sends 
> the stored container report, which is the one that failed or a newer one. 
> Node and pipeline reports are left as they are, since their publishers 
> refresh them every 60s by default. The container report now rides on every 
> following heartbeat until one succeeds. If the heartbeat failed after SCM 
> processed it, SCM receives the same report twice, which it handles as an 
> ordinary repeated report. A heartbeat that fails because it is too large (the 
> situation of HDDS-16723) keeps carrying the report and can keep failing 
> longer than before, when only the ICR backlog was retried.
> Covered by {{testFailedHeartbeatResendsContainerReport}} in the existing 
> {{TestHeartbeatEndpointTask}}: after a failed heartbeat, the next one must 
> carry the container report again and the one after must not, and a later 
> failed heartbeat that carried no container report must not cause one to be 
> sent. Without the change it fails because the second heartbeat has no 
> container report, and without the check that the failed request carried the 
> report it fails because the fourth heartbeat carries one. With it 
> {{TestHeartbeatEndpointTask}}, {{TestStateContext}}, {{TestContainerSet}}, 
> {{TestReportPublisher}}, {{TestVolumeSetDiskChecks}}, {{TestOzoneContainer}}, 
> {{TestDatanodeStateMachine}} and {{TestRunningDatanodeState}} (108 tests) and 
> {{TestEndPoint}} in server-scm (18 tests) pass, checkstyle is clean. With the 
> patch the reproduction fails, because Recon receives the FCR again with the 
> next heartbeat and removes the replica.
> Found by TLA+ model checking and code review of the datanode container report 
> and Recon container tracking paths under HDDS-15926, on commit 
> 916aa1d9949cbb10f2750a40c8704340893131a2. 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