From 393c90d11030006fbd6e8451d9aff924d6435b94 Mon Sep 17 00:00:00 2001 From: navinko Date: Fri, 18 Sep 2026 22:36:44 +0530 Subject: [PATCH 1/3] HDDS-16386. NodeDecommissionManager should read maintenance replica config from ReplicationManagerConfiguration --- .../scm/node/NodeDecommissionManager.java | 17 +++++++-- .../scm/node/TestNodeDecommissionManager.java | 36 +++++++++++++++++-- 2 files changed, 48 insertions(+), 5 deletions(-) diff --git a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/node/NodeDecommissionManager.java b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/node/NodeDecommissionManager.java index e279a63a4ae1..f1f2fe4612ce 100644 --- a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/node/NodeDecommissionManager.java +++ b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/node/NodeDecommissionManager.java @@ -282,8 +282,11 @@ public NodeDecommissionManager(OzoneConfiguration config, NodeManager nm, Contai ScmConfigKeys.OZONE_SCM_DATANODE_ADMIN_MONITOR_INTERVAL_DEFAULT, TimeUnit.MILLISECONDS); - setMaintenanceConfigs(config.getInt("hdds.scm.replication.maintenance.replica.minimum", 2), - config.getInt("hdds.scm.replication.maintenance.remaining.redundancy", 1)); + // Read the maintenance replica config from ReplicationManagerConfiguration, + // which is the single source of truth for these values. + ReplicationManager.ReplicationManagerConfiguration rmConf = rm.getConfig(); + setMaintenanceConfigs(rmConf.getMaintenanceReplicaMinimum(), + rmConf.getMaintenanceRemainingRedundancy()); monitor = new DatanodeAdminMonitorImpl(config, eventQueue, nodeManager, rm); @@ -645,4 +648,14 @@ public void setMaintenanceConfigs(int replicaMinimum, int remainingRedundancy) { maintenanceReplicaMinimum = replicaMinimum; } } + + @VisibleForTesting + public synchronized int getMaintenanceReplicaMinimum() { + return maintenanceReplicaMinimum; + } + + @VisibleForTesting + public synchronized int getMaintenanceRemainingRedundancy() { + return maintenanceRemainingRedundancy; + } } diff --git a/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/node/TestNodeDecommissionManager.java b/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/node/TestNodeDecommissionManager.java index 4a3ccecd7257..e49b5b79a71f 100644 --- a/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/node/TestNodeDecommissionManager.java +++ b/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/node/TestNodeDecommissionManager.java @@ -51,6 +51,7 @@ import org.apache.hadoop.hdds.scm.container.ContainerID; import org.apache.hadoop.hdds.scm.container.ContainerInfo; import org.apache.hadoop.hdds.scm.container.ContainerManager; +import org.apache.hadoop.hdds.scm.container.replication.ReplicationManager; import org.apache.hadoop.hdds.scm.ha.SCMContext; import org.apache.hadoop.hdds.scm.node.states.NodeNotFoundException; import org.apache.hadoop.hdds.scm.pipeline.PipelineID; @@ -80,7 +81,7 @@ void setup(@TempDir File dir) throws Exception { nodeManager = (SCMNodeManager) scm.getScmNodeManager(); containerManager = mock(ContainerManager.class); decom = new NodeDecommissionManager(conf, nodeManager, containerManager, - SCMContext.emptyContext(), new EventQueue(), null); + SCMContext.emptyContext(), new EventQueue(), mockReplicationManager(conf)); when(containerManager.allocateContainer(any(ReplicationConfig.class), anyString())) .thenAnswer(invocation -> createMockContainer((ReplicationConfig)invocation.getArguments()[0], (String) invocation.getArguments()[1])); @@ -111,6 +112,13 @@ private ContainerInfo getMockContainer(ReplicationConfig rep, ContainerID conId) return builder.build(); } + private ReplicationManager mockReplicationManager(OzoneConfiguration config) { + ReplicationManager rm = mock(ReplicationManager.class); + when(rm.getConfig()).thenReturn( + config.getObject(ReplicationManager.ReplicationManagerConfiguration.class)); + return rm; + } + @Test public void testHostStringsParseCorrectly() throws InvalidHostStringException { @@ -612,7 +620,7 @@ public void testInsufficientNodeDecommissionChecksForNNF() throws nodeManager = mock(SCMNodeManager.class); decom = new NodeDecommissionManager(conf, nodeManager, containerManager, - SCMContext.emptyContext(), new EventQueue(), null); + SCMContext.emptyContext(), new EventQueue(), mockReplicationManager(conf)); when(containerManager.getContainer(any(ContainerID.class))) .thenAnswer(invocation -> getMockContainer(RatisReplicationConfig .getInstance(HddsProtos.ReplicationFactor.THREE), (ContainerID)invocation.getArguments()[0])); @@ -970,7 +978,7 @@ public void testInsufficientNodeMaintenanceChecksForNNF() throws nodeManager = mock(SCMNodeManager.class); decom = new NodeDecommissionManager(conf, nodeManager, containerManager, - SCMContext.emptyContext(), new EventQueue(), null); + SCMContext.emptyContext(), new EventQueue(), mockReplicationManager(conf)); when(containerManager.getContainer(any(ContainerID.class))) .thenAnswer(invocation -> getMockContainer(RatisReplicationConfig .getInstance(HddsProtos.ReplicationFactor.THREE), (ContainerID)invocation.getArguments()[0])); @@ -1101,4 +1109,26 @@ private List generateDatanodes() { return dns; } + + @Test + public void testMaintenanceConfigsReadFromReplicationManagerConfiguration() { + // NodeDecommissionManager built in setup() uses the ReplicationManagerConfiguration defaults (2, 1). + assertEquals(2, decom.getMaintenanceReplicaMinimum()); + assertEquals(1, decom.getMaintenanceRemainingRedundancy()); + + // Build a ReplicationManagerConfiguration with non-default maintenance values. + ReplicationManager.ReplicationManagerConfiguration rmConf = + new OzoneConfiguration().getObject(ReplicationManager.ReplicationManagerConfiguration.class); + rmConf.setMaintenanceReplicaMinimum(5); + rmConf.setMaintenanceRemainingRedundancy(3); + ReplicationManager rm = mock(ReplicationManager.class); + when(rm.getConfig()).thenReturn(rmConf); + + NodeDecommissionManager manager = new NodeDecommissionManager(conf, nodeManager, + containerManager, SCMContext.emptyContext(), new EventQueue(), rm); + + // Constructor must read these from ReplicationManagerConfiguration, not hardcoded defaults. + assertEquals(5, manager.getMaintenanceReplicaMinimum()); + assertEquals(3, manager.getMaintenanceRemainingRedundancy()); + } } From 428e054d95163c5f892661983ef06d36e7b59f1e Mon Sep 17 00:00:00 2001 From: navinko Date: Sat, 19 Sep 2026 11:16:24 +0530 Subject: [PATCH 2/3] HDDS-16386. Fix TestDeletedBlockLog NPE from mocked ReplicationManager --- .../org/apache/hadoop/hdds/scm/block/TestDeletedBlockLog.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/block/TestDeletedBlockLog.java b/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/block/TestDeletedBlockLog.java index 2bfa5f379b78..4578c676f2d1 100644 --- a/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/block/TestDeletedBlockLog.java +++ b/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/block/TestDeletedBlockLog.java @@ -121,6 +121,10 @@ public void setup() throws Exception { conf = new OzoneConfiguration(); conf.set(HddsConfigKeys.OZONE_METADATA_DIRS, testDir.getAbsolutePath()); replicationManager = mock(ReplicationManager.class); + // This test runs a real SCM, whose startup builds a NodeDecommissionManager that reads + // rm.getConfig(), so the injected mock RM must return a real config, not null. + when(replicationManager.getConfig()).thenReturn( + conf.getObject(ReplicationManager.ReplicationManagerConfiguration.class)); SCMConfigurator configurator = new SCMConfigurator(); configurator.setSCMHAManager(SCMHAManagerStub.getInstance(true)); configurator.setReplicationManager(replicationManager); From bd73aaad06fc838d6fd26b0ae519920f7a2f0131 Mon Sep 17 00:00:00 2001 From: navinko Date: Mon, 21 Sep 2026 20:34:02 +0530 Subject: [PATCH 3/3] HDDS-16386. Addressed Review comments. --- .../apache/hadoop/hdds/scm/node/NodeDecommissionManager.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/node/NodeDecommissionManager.java b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/node/NodeDecommissionManager.java index 8d4ef2f74121..6b5db5c14f4c 100644 --- a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/node/NodeDecommissionManager.java +++ b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/node/NodeDecommissionManager.java @@ -281,8 +281,6 @@ public NodeDecommissionManager(OzoneConfiguration config, NodeManager nm, Contai ScmConfigKeys.OZONE_SCM_DATANODE_ADMIN_MONITOR_INTERVAL_DEFAULT, TimeUnit.MILLISECONDS); - // Read the maintenance replica config from ReplicationManagerConfiguration, - // which is the single source of truth for these values. ReplicationManager.ReplicationManagerConfiguration rmConf = rm.getConfig(); setMaintenanceConfigs(rmConf.getMaintenanceReplicaMinimum(), rmConf.getMaintenanceRemainingRedundancy());