Siyao Meng created HDDS-16892:
---------------------------------

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