Siyao Meng created HDDS-17060:
---------------------------------

             Summary: Recon keeps a container CLOSED after SCM moves it from 
DELETING or DELETED back to QUASI_CLOSED
                 Key: HDDS-17060
                 URL: https://issues.apache.org/jira/browse/HDDS-17060
             Project: Apache Ozone
          Issue Type: Bug
            Reporter: Siyao Meng
         Attachments: MC-8-follow-scm-from-closed-back-to-quasi-closed.patch, 
TestBugMC8ResurrectionNotMirrored.java

h3. Mechanism
SCM's replication manager moves an empty CLOSED container to DELETING and, once 
no replica is left, to DELETED. If a datanode then reports a non-empty Ratis 
replica that is OPEN, CLOSING, UNHEALTHY, or QUASI_CLOSED with a bcsId above 
the container's, {{AbstractContainerReportHandler.updateContainerState}} moves 
the container back to QUASI_CLOSED (HDDS-14684, which extended HDDS-12421). 
Recon does not follow when it still has the container CLOSED at that point:
* {{ReconStorageContainerSyncHelper}} never copies DELETING, and copies DELETED 
only from SCM's current DELETED list, which no longer contains the container.
* Recon's report handler runs the same {{updateContainerState}}, which does 
nothing for a CLOSED container.
* {{ReconStorageContainerSyncHelper.reconcileToQuasiClosed}} handles a 
container that SCM lists as QUASI_CLOSED only when Recon has it DELETED, OPEN 
or CLOSING. For CLOSED it logs at debug level and skips it, on every cycle.

The skip is deliberate 
({{TestReconSCMContainerSyncIntegration.doesNotOverwriteContainerAlreadyClosedInRecon}}):
 Recon can be ahead of SCM when it processed a CLOSED replica that SCM has not 
processed yet. But SCM never moves a CLOSED container to QUASI_CLOSED directly, 
only through DELETING or DELETED, so in this case SCM is ahead.

h3. Trigger
# An empty CLOSED container; a datanode that holds a stale non-empty copy is 
away.
# SCM's replication manager deletes the empty replicas and moves the container 
to DELETING (then DELETED once the deletes are confirmed).
# The datanode comes back and reports its replica (UNHEALTHY in the 
reproduction) before Recon's next sync has run its DELETED pass. SCM moves the 
container back to QUASI_CLOSED.

The window runs from SCM's DELETE transition until Recon's next sync cycle has 
run its DELETED pass ({{ozone.recon.scm.container.sync.task.interval.delay}}, 
default 6h); whether SCM is in DELETING or already in DELETED does not matter. 
If Recon has already synced DELETED, its own report handler moves the container 
back to QUASI_CLOSED together with SCM. If SCM later closes the container (a 
CLOSED replica appears) or deletes it again, the CLOSED or DELETED pass catches 
up. With only an UNHEALTHY replica left, SCM keeps it QUASI_CLOSED.

h3. Impact
Recon shows the container as CLOSED and leaves it out of its QUASI_CLOSED list 
and count ({{ContainerEndpoint.getQuasiClosedContainers}}) for as long as SCM 
keeps it QUASI_CLOSED. Recon's QUASI_CLOSED specific health states cannot apply 
to it (argued from the code, not run). SCM and datanode state are not affected. 
The same code is in 2.2.0 and 2.2.1. Recon replaces its SCM DB from a snapshot 
only at startup with a large count difference or on an admin request.

h3. Reproduction
PASS, deterministic, unmodified source. 
[^TestBugMC8ResurrectionNotMirrored.java] ({{hadoop-ozone/recon}}) runs a real 
SCM {{ContainerManagerImpl}} and SCM {{IncrementalContainerReportHandler}} next 
to the real {{ReconContainerManager}}, 
{{ReconIncrementalContainerReportHandler}} and 
{{ReconStorageContainerSyncHelper}}. The SCM client that Recon calls answers 
from the SCM container manager. Every datanode report goes to both sides; the 
only step applied directly is the {{DELETE}} (and {{CLEANUP}}) transition the 
replication manager makes. Two tests end, after six sync cycles and a repeated 
report, with SCM QUASI_CLOSED and Recon CLOSED: one with the report arriving 
while SCM is DELETING, one while SCM is already DELETED but before Recon's next 
sync. A control test where Recon synced DELETED first shows Recon following 
SCM. A passing test means the defect is present.

h3. Suggested fix
A rule that waits for two consecutive sync cycles is not needed. When Recon is 
ahead, it got there by closing the container on a CLOSED replica, which it 
records. So Recon can follow SCM back to QUASI_CLOSED whenever SCM lists the 
container QUASI_CLOSED and Recon knows no CLOSED replica of it. This is a 
heuristic with bounded, self healing errors: if Recon was ahead and has lost 
its CLOSED replica record (for example the node died), it follows SCM to 
QUASI_CLOSED and closes again through the CLOSED pass or the next CLOSED 
replica report once SCM closes the container; a stale CLOSED replica record 
delays the move until a report removes it.

h3. Patch
[^MC-8-follow-scm-from-closed-back-to-quasi-closed.patch], against 
916aa1d9949cbb10f2750a40c8704340893131a2. It also applies to master at 
62d982ff2d9.

In {{reconcileToQuasiClosed}}, a container that Recon has CLOSED and SCM lists 
as QUASI_CLOSED is moved to QUASI_CLOSED when none of its replicas in Recon is 
CLOSED. It uses the same transitions SCM went through ({{DELETE}}, then 
{{transitionDeletingOrDeletedToTargetState}}), so the record and its replicas 
are kept. A container with a CLOSED replica is still skipped as before.

Covered by the new {{followsScmBackToQuasiClosedWhenReconHasNoClosedReplica}} 
in {{TestReconSCMContainerSyncIntegration}}; 
{{doesNotOverwriteContainerAlreadyClosedInRecon}} now gives Recon the CLOSED 
replica that makes it ahead of SCM, and still expects CLOSED. Without the 
change the new test fails with Recon CLOSED; with it the Recon {{scm}} package 
tests pass and checkstyle is clean. With the patch the two defect tests of the 
reproduction fail because Recon ends QUASI_CLOSED, and the control still passes.

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