Repository navigation
Conversation
…e-registers while SCM is removing it as DEAD
smengcl
marked this pull request as ready for review
October 24, 2026 05:40
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What changes were proposed in this pull request?
SCM picks datanodes for pipelines and new replicas from its network topology. Two pieces of code change the topology for a datanode that is already registered:
NodeStateManager#checkNodesHealth) removes a datanode from the topology when it marks the datanode DEAD.SCMNodeManager#registerreplaces the datanode's object in the topology. Then it replaces the datanode'sDatanodeInfo.These two run on different threads, and no lock keeps them apart. So they can interleave like this:
Now the datanode is HEALTHY, but it is not in the topology, so placement never picks it. It stays out until it becomes DEAD and recovers, or until SCM restarts.
NetworkTopology#containsstill returns true for it, because the datanode's parent pointer still points to the rack.The other order breaks too. Say the registration has put the new object in the topology, but has not stored the new
DatanodeInfoyet. If the health check marks the datanode DEAD at that moment, it removes the new object. Then the registration stores the HEALTHYDatanodeInfo, and the datanode ends up the same way.The window is small: the datanode has to register again at the same moment SCM marks it DEAD.
With this change, the health check and the registration do not lock each other out, because the race is rare. Instead, each one checks for the other:
NodeStateMap#updateNodeHealthStatechecks that the map still holds the sameDatanodeInfoobject, with the same health. A registration that changes the datanode puts a newDatanodeInfoin the map, so a decision made on the old one is skipped. The next health check decides again.NodeStateManager#updateNodenow does the topology update, instead ofSCMNodeManager#register. It puts the newDatanodeInfoitself in the topology, before it replaces the old one in the node state.DatanodeInfo. If the topology does not hold the newDatanodeInfo, or holds the old one again, it updates the topology again.DatanodeInfo, the health check puts that one in the topology.Whichever side checks last sees the change of the other side. So the topology always ends up with the new
DatanodeInfo.An earlier version of this PR made
updateNodeandcheckNodesHealthsynchronized. That held a lock during every health check, only to guard against a rare race. This version checks for the race instead.The bug was found while testing HDDS-16627 (#11349), the deterministic SCM simulation.
What is the link to the Apache JIRA
https://issues.apache.org/jira/browse/HDDS-16633
How was this patch tested?
New test
TestSCMNodeManager#testRegisterWhileMarkedDeadKeepsNodeInTopology. It runs the health check and a registration with new ports on two threads, in two cases:At the end, the datanode must be HEALTHY and in the topology, with its new ports. Without the fix, it ends up HEALTHY but not in the topology. The first case also fails without the check in the health check, and the second case fails without the check in the registration.
New test
TestNodeStateMap#testUpdateHealthSkipsChangedNodechecks that the health is not changed when the node has changed since the caller read it.