Siyao Meng created HDDS-17064:
---------------------------------

             Summary: 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
         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