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

Siyao Meng updated HDDS-16892:
------------------------------
    Attachment: MC-9-keep-pending-allocations-on-datanode-reregistration.patch

> SCM drops the pending container allocations of a datanode that re-registers 
> with a new version or port set, so its last slot can be granted twice
> -------------------------------------------------------------------------------------------------------------------------------------------------
>
>                 Key: HDDS-16892
>                 URL: https://issues.apache.org/jira/browse/HDDS-16892
>             Project: Apache Ozone
>          Issue Type: Bug
>            Reporter: Siyao Meng
>            Priority: Minor
>         Attachments: 
> MC-9-keep-pending-allocations-on-datanode-reregistration.patch, 
> TestBugMC9ReRegisterDropsPendingAllocations.java
>
>
> h3. Mechanism
> When a registered datanode registers again with the same ID but a changed 
> version string or port set, {{SCMNodeManager.register}} calls 
> {{updateNodeAndTopology}}, and {{NodeStateManager.updateNode}} replaces the 
> stored {{DatanodeInfo}} with a new object. The {{DatanodeInfo}} constructor 
> creates a new, empty {{TwoWindowBucket}}, and nothing copies the entries of 
> the old one. Pending allocations of containers that SCM has allocated on the 
> datanode but that the datanode has not created yet (containers are created on 
> the first write) are therefore lost. Once the next heartbeat brings a node 
> report, {{hasAvailableSpace}} and {{checkSpaceAndRecordAllocation}} see the 
> slot as free and grant it again. The old bucket stays reachable only through 
> stale references, and later container reports update the new one.
> An address change takes the same path, but there {{NODE_ADDRESS_UPDATE}} 
> makes {{PipelineManagerImpl.closeStalePipelines}} close the pipelines with 
> the old address, so the dropped entries belong to containers that can no 
> longer be created there.
> h3. Trigger
> # A datanode has room for one more container, and SCM allocates container C1 
> on it.
> # Before C1 is written, the datanode restarts and re-registers with a new 
> version (for example in a rolling upgrade), within the stale node interval so 
> its pipeline stays open. A re-registration with a changed port set takes the 
> same code path.
> # The next node report arrives, and SCM allocates or places C2 on the same 
> datanode.
> h3. Impact
> The datanode is promised more containers than it has room for, which is what 
> {{PendingContainerTracker}} exists to prevent. Only reservations that are 
> younger than two roll intervals 
> ({{ozone.scm.pending.container.roll.interval}}, default 5m) and not yet 
> confirmed by a container report are affected, and only datanodes that are 
> down to their last free slots can be over granted. The expected effect is 
> that the datanode refuses one of the containers for lack of space when it is 
> created. On a Ratis pipeline the failed create marks the pipeline's 
> {{ContainerStateMachine}} unhealthy, so the pipeline is closed and the in 
> flight writes on it are retried elsewhere. This is argued from the code and 
> was not run.
> h3. Reproduction
> [^TestBugMC9ReRegisterDropsPendingAllocations.java] 
> ({{hadoop-hdds/server-scm}}) uses a real SCM from {{HddsTestUtils.getScm}} 
> with a 1GB container size and one datanode with 1.5GB usable, so exactly one 
> slot. It records C1 through 
> {{PipelineManagerImpl.checkSpaceAndRecordAllocation}} (the call 
> {{ContainerManagerImpl.allocateContainer}} makes), checks that the slot is 
> taken, re-registers the datanode with a new version or a new port set, 
> processes the next node report and asserts that the slot is still taken. On 
> an unmodified checkout both cases fail at that check ({{slot held by C1 must 
> not look free}}), so the second allocation would be granted. With the patch 
> both pass.
> h3. Patch
> [^MC-9-keep-pending-allocations-on-datanode-reregistration.patch], against 
> b0aa6475b7856d2c7f467dd3db12b9a1dd116f63. It also applies to master at 
> 6818707698c.
> {{DatanodeInfo}} gets a package private constructor that takes an existing 
> {{TwoWindowBucket}}, and {{NodeStateManager.updateNode}} passes the bucket of 
> the info it replaces. The same instance is shared, so a thread that still 
> holds the old {{DatanodeInfo}} records into the live bucket, and the report 
> that confirms the container removes the entry as usual. Everything else in 
> the replacement is reset as before. For an address change the entries are now 
> kept as well, although their pipelines are closed. That is the conservative 
> direction: it can only under grant on that datanode, for at most two roll 
> intervals, after which the entries age out like any unconfirmed entry.
> Covered by 
> {{TestSCMNodeManager#testRegisterKeepsPendingAllocationsOnVersionOrPortChange}},
>  which fails without the change ({{expected: <1> but was: <0>}} for the 
> pending count after the version change). With the change 118 tests in the 
> node package suites pass, and checkstyle is clean.
> Found by TLA+ model checking and code review of the SCM pending container 
> allocation tracking 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