Siyao Meng created HDDS-17063:
---------------------------------

             Summary: ICR sent after the volume failure full container report 
re-adds the lost replica in Recon and SCM
                 Key: HDDS-17063
                 URL: https://issues.apache.org/jira/browse/HDDS-17063
             Project: Apache Ozone
          Issue Type: Bug
            Reporter: Siyao Meng
         Attachments: MC-3-no-icr-for-container-on-failed-volume.patch, 
TestBugMC3LateIcrAfterVolumeFailure.java

h3. Mechanism
When a volume fails, {{ContainerSet.handleVolumeFailures}} removes its 
containers, then {{StateContext.getFullContainerReportDiscardPendingICR}} drops 
the queued ICRs and builds a full container report (FCR) without them. A thread 
that already holds one of these containers past its lock section, such as 
{{KeyValueHandler.closeContainer}} after the container is closed, or 
{{quasiCloseContainer}} and {{markContainerForClose}}, can still call the ICR 
sender afterwards. The sender builds the ICR from the container object without 
checking the volume, and it is queued after the FCR. The next heartbeat carries 
the FCR without the container followed by the ICR, so the receiver removes the 
replica and then adds it back.

The drop of queued ICRs is meant to prevent this ("Block ICRs from being 
generated"), but it synchronizes on the {{OzoneContainer}} object while the ICR 
sender synchronizes on the {{ContainerSet}}, so they do not exclude each other. 
Only the FCR build in {{ContainerSet.getContainerReport}} takes the container 
set monitor, and any ICR still queued at that point is sent after the FCR 
anyway.

h3. Trigger
# SCM asks the datanode to close a container; {{markContainerForClose}} moves 
it to CLOSING.
# {{closeContainer}} closes it and releases the container lock, and is about to 
send the ICR.
# The volume holding it fails its health check; {{handleVolumeFailures}} 
removes the container and queues an FCR without it.
# {{closeContainer}} sends the CLOSED ICR.

No fault other than the disk failure is needed; the window is a few statements 
wide.

h3. Impact
* Recon lists a CLOSED replica on the datanode that no longer holds the 
container (run). The next periodic FCR from that datanode removes it, 60 to 120 
minutes after the previous one ({{hdds.container.report.interval}} plus a 
random delay of up to the same interval).
* SCM processes the ICR with the same handler code, so it counts the lost 
replica for the same time and does not re-replicate that container until then 
(argued from the code, not run).
* One container per occurrence.

h3. Reproduction
PASS, deterministic, unmodified source. 
[^TestBugMC3LateIcrAfterVolumeFailure.java] ({{hadoop-ozone/recon}}) uses the 
real datanode {{OzoneContainer}}, {{ContainerController}}, {{KeyValueHandler}}, 
{{MutableVolumeSet}}, {{StorageVolumeChecker}} and {{StateContext}}. The volume 
fails for real: the test removes the permissions of its directory and the 
volume checker fails it. The only timing aid is a log4j appender on the 
{{ContainerLog}} logger that parks the close thread at 
{{ContainerLogger.logClosed}}, after the lock is released and right before the 
ICR is sent, until the volume failure has been handled. The reports are applied 
in heartbeat order to the real Recon FCR and ICR handlers. The heartbeat 
carries the FCR without the container followed by its CLOSED ICR, and Recon 
ends with a CLOSED replica that the datanode does not have. It must run as a 
user that file permissions apply to (not root). A passing test means the defect 
is present.

h3. Patch
[^MC-3-no-icr-for-container-on-failed-volume.patch], against 
916aa1d9949cbb10f2750a40c8704340893131a2. It also applies to master at 
62d982ff2d9. The open pull request for HDDS-16723 (apache/ozone#11423) also 
changes {{StateContext}}; the main code of this patch applies cleanly on its 
head commit.

The ICR sender in {{OzoneContainer}} sends nothing for a container whose volume 
has failed (the rule {{KeyValueHandler.markContainerUnhealthy}} already 
follows: "The container has already been discarded and SCM notified"), and 
{{getFullContainerReportDiscardPendingICR}} now synchronizes on the container 
set, the monitor the ICR sender holds. The volume is marked failed before 
{{handleVolumeFailures}} runs, so either the sender sees the failed volume, or 
it queues its ICR before the drop starts and the drop removes it. Checking 
instead whether the container is still in the container set would suppress the 
DELETED ICR, which is sent after the container is removed from the set, and 
would not be ordered against the drop. The volume check can suppress a DELETED 
ICR only when the volume fails between the delete's own failed volume check and 
its ICR; if no other container is left on a failed volume, no FCR follows 
either, and SCM keeps the replica (and resends the delete) until the next 
periodic FCR (argued, not run). A container added to a volume after it failed 
(HDDS-17018) sends no ICRs with this patch, but is still in the FCR until that 
issue is fixed.

Covered by two tests in existing classes. 
{{TestStateContext.testFullContainerReportDropsIcrQueuedWhileContainerSetLocked}}
 queues an ICR while holding the container set monitor and the FCR build waits 
on it; without the change the ICR survives the build. 
{{TestOzoneContainer.testNoIncrementalReportForContainerOnFailedVolume}} fails 
the volume of a real container and then quasi closes it; without the change a 
second ICR is sent. With the change the container service suites around 
reports, volumes, close and delete commands and the container handlers (365 
tests in 13 classes), {{TestDatanodeHddsVolumeFailureDetection}} and 
{{TestDatanodeHddsVolumeFailureToleration}} on a mini cluster, and 
{{TestEndPoint}} in server-scm pass, checkstyle is clean. With the patch the 
reproduction fails, because the heartbeat carries only the FCR.

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