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

Siyao Meng updated HDDS-17218:
------------------------------
    Attachment: MC-6-wait-for-stale-replica-node-before-admin-completion.patch

> Datanode can enter maintenance below the replica minimum because a replica on 
> a STALE datanode still counts as an available copy
> --------------------------------------------------------------------------------------------------------------------------------
>
>                 Key: HDDS-17218
>                 URL: https://issues.apache.org/jira/browse/HDDS-17218
>             Project: Apache Ozone
>          Issue Type: Bug
>            Reporter: Siyao Meng
>            Priority: Minor
>         Attachments: 
> MC-6-wait-for-stale-replica-node-before-admin-completion.patch, 
> TestBugMC6StalePeerMaintenance.java
>
>
> h3. Mechanism
> {{DatanodeAdminMonitorImpl.checkContainersReplicatedOnNode}} relies on 
> {{ReplicationManager.checkContainerStatus}} to decide whether enough copies 
> remain elsewhere. {{RatisContainerReplicaCount.countReplicas}} classifies 
> each replica only by the persisted operational state of its datanode, so a 
> replica on an IN_SERVICE datanode that SCM has already marked STALE is 
> counted as a healthy copy until the datanode is DEAD and {{DeadNodeHandler}} 
> removes its replicas. The monitor does not look at the health of the other 
> replica holders either; after the check it only re-reads the health of the 
> node it is processing.
> Replication Manager counts a replica on a STALE datanode until the datanode 
> is DEAD. The over replication handlers ({{RatisOverReplicationHandler}}, 
> {{ECOverReplicationHandler}}, {{QuasiClosedStuckOverReplicationHandler}}) 
> however leave STALE replicas out before they choose a replica to delete, 
> because the STALE node may be about to die. Taking a node offline for 
> maintenance or decommission removes a reachable copy in the same way, but the 
> admin check has no such guard.
> h3. Trigger
> # A closed RATIS/THREE container has replicas on A, B and C. D is another 
> datanode.
> # B stops. SCM marks it STALE (after {{ozone.scm.stale.node.interval}}, 5m) 
> but not yet DEAD ({{ozone.scm.dead.node.interval}}, 10m).
> # Within that window an admin puts A into maintenance. The admission check 
> passes because A, C and D are IN_SERVICE and HEALTHY, which leaves two after 
> A, meeting the minimum of 2.
> # The check counts B and C as two healthy copies, which meets 
> {{hdds.scm.replication.maintenance.replica.minimum}} (2). A becomes 
> IN_MAINTENANCE and no copy is made on D.
> Decommission uses the same check, so with a STALE B it completes with only 
> two reachable in service copies (argued from the code and covered by the unit 
> test, not run on a cluster).
> h3. Impact
> A is in maintenance with one reachable copy (C), one below the configured 
> minimum. If C fails before B is declared DEAD and Replication Manager has 
> made a new copy on D, the data is unreadable until A leaves maintenance or B 
> comes back. No data is lost, A keeps its copy. The window is bounded by the 
> STALE period; once B is DEAD, Replication Manager makes the second copy on D 
> from C (or from A while it is still running). The same check is in 
> ozone-2.1.2, ozone-2.2.0 and ozone-2.2.1 (read from the tagged source, not 
> run on a release).
> h3. Reproduction
> PASS on unmodified source. [^TestBugMC6StalePeerMaintenance.java] 
> ({{hadoop-ozone/integration-test}}) runs a mini cluster with four datanodes 
> and the settings of {{TestDecommissionAndMaintenance}}, except that the dead 
> interval is 60s so that the STALE window is wide enough for the 1s monitor 
> interval. It writes one RATIS/THREE key, closes its container, stops B, waits 
> for STALE, calls {{startMaintenanceNodes}} for A without force through 
> {{ContainerOperationClient}}, then stops A and C and reads the key. Run on 
> the unmodified source: A reached IN_MAINTENANCE 7.8s after B became STALE, 
> with B still STALE and one IN_SERVICE HEALTHY replica, and the read failed 
> with GetBlock errors on all replicas. A passing test means the defect is 
> present.
> h3. Patch
> [^MC-6-wait-for-stale-replica-node-before-admin-completion.patch], against 
> b0aa6475b7856d2c7f467dd3db12b9a1dd116f63. It also applies to master at 
> 20d95ed37fe.
> The monitor treats a container as not yet replicated while any of its 
> replicas is on an IN_SERVICE datanode that is not HEALTHY (STALE, or DEAD 
> before {{DeadNodeHandler}} has removed its replicas). The node then waits 
> until that datanode is HEALTHY again, or DEAD with its replicas removed, 
> after which the normal count applies and Replication Manager makes the 
> missing copy. Each wait ends within the dead interval: the node completes at 
> the first monitor run in which no IN_SERVICE datanode holding one of its 
> replicas is STALE or DEAD. Replicas on datanodes in maintenance or 
> decommission are ignored: they are not counted as available copies, and a 
> DEAD node in maintenance keeps its replicas (HDDS-2607), so waiting for it 
> would not end. The check is in the monitor, not in the replica counts, so 
> Replication Manager's own handling of STALE replicas does not change. It 
> applies to EC replicas as well (not tested with an EC container). With a 
> STALE replica holder the node now waits even if the container would have 
> enough copies without that replica, for example with surplus replicas, or 
> when the replica on the STALE datanode is one that is not counted anyway, 
> such as an UNHEALTHY one. While it waits, the container is counted as under 
> replicated in the decommission metrics and listed as under replicated by 
> {{ozone admin datanode status decommission}}, although Replication Manager 
> does not report it so. The monitor log names the datanode it waits for 
> ("Container ... waits for its replica on ..., which is IN_SERVICE but not 
> HEALTHY") in place of the usual under replicated message, within the existing 
> limit on logged containers.
> Covered in the existing {{TestDatanodeAdminMonitor}} by 
> {{testAdminWaitsWhileReplicaIsOnStaleNode}} (decommission and maintenance, 
> the other replica holders registered as IN_SERVICE and HEALTHY), 
> {{testDecommissionWaitsForReplicaOnDeadInServiceNodeToBeRemoved}} (a replica 
> on a DEAD IN_SERVICE datanode holds the node until it is removed) and the 
> negative case {{testMaintenanceNotBlockedByReplicaOnDeadMaintenanceNode}}. 
> Without the change the first two fail with "expected: <ENTERING_MAINTENANCE> 
> but was: <IN_MAINTENANCE>" and "expected: <DECOMMISSIONING> but was: 
> <DECOMMISSIONED>". With it {{TestDatanodeAdminMonitor}}, 
> {{TestNodeDecommissionMetrics}} and {{TestNodeDecommissionManager}} pass, as 
> does the mini cluster suite {{TestDecommissionAndMaintenance}} (9 tests), 
> checkstyle is clean, and in the reproduction A stays ENTERING_MAINTENANCE 
> until B is DEAD and enters maintenance with two reachable copies, 61s after B 
> became STALE.
> Found by TLA+ model checking and code review of the SCM datanode decommission 
> and maintenance admin 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: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to