diff --git a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/pipeline/PipelinePlacementPolicy.java b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/pipeline/PipelinePlacementPolicy.java index f1f325ce1fca..c43373875a70 100644 --- a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/pipeline/PipelinePlacementPolicy.java +++ b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/pipeline/PipelinePlacementPolicy.java @@ -22,6 +22,7 @@ import com.google.common.base.Preconditions; import org.apache.hadoop.hdds.client.RatisReplicationConfig; import org.apache.hadoop.hdds.conf.ConfigurationSource; +import org.apache.hadoop.hdds.conf.StorageUnit; import org.apache.hadoop.hdds.protocol.DatanodeDetails; import org.apache.hadoop.hdds.protocol.proto.HddsProtos; import org.apache.hadoop.hdds.protocol.proto.HddsProtos.ReplicationFactor; @@ -45,9 +46,10 @@ * and network topology to supply pipeline creation. *

* 1. get a list of healthy nodes - * 2. filter out nodes that are not too heavily engaged in other pipelines - * 3. Choose an anchor node among the viable nodes. - * 4. Choose other nodes around the anchor node based on network topology + * 2. filter out nodes that have space less than container size. + * 3. filter out nodes that are not too heavily engaged in other pipelines + * 4. Choose an anchor node among the viable nodes. + * 5. Choose other nodes around the anchor node based on network topology */ public final class PipelinePlacementPolicy extends SCMCommonPlacementPolicy { @VisibleForTesting @@ -151,11 +153,30 @@ List filterViableNodes( SCMException.ResultCodes.FAILED_TO_FIND_SUITABLE_NODE); } + long sizeRequired = (long) conf.getStorageSize( + ScmConfigKeys.OZONE_SCM_CONTAINER_SIZE, + ScmConfigKeys.OZONE_SCM_CONTAINER_SIZE_DEFAULT, + StorageUnit.BYTES); + + // filter nodes that don't even have space for one container + List canHoldList = healthyNodes.stream().filter(d -> + hasEnoughSpace(d, sizeRequired)).collect(Collectors.toList()); + + if (canHoldList.size() < nodesRequired) { + msg = String.format("Pipeline creation failed due to no sufficient" + + " healthy datanodes with enough space for even a single container." + + " Required %d. Found %d. Container size %d.", + nodesRequired, canHoldList.size(), sizeRequired); + LOG.warn(msg); + throw new SCMException(msg, + SCMException.ResultCodes.FAILED_TO_FIND_SUITABLE_NODE); + } + // filter nodes that meet the size and pipeline engagement criteria. // Pipeline placement doesn't take node space left into account. // Sort the DNs by pipeline load. // TODO check if sorting could cause performance issue: HDDS-3466. - List healthyList = healthyNodes.stream() + List healthyList = canHoldList.stream() .map(d -> new DnWithPipelines(d, currentPipelineCount(d, nodesRequired))) .filter(d -> diff --git a/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/node/TestDeadNodeHandler.java b/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/node/TestDeadNodeHandler.java index d22b2d426dfa..7371e40cdebd 100644 --- a/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/node/TestDeadNodeHandler.java +++ b/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/node/TestDeadNodeHandler.java @@ -61,6 +61,7 @@ import org.apache.hadoop.hdds.server.events.EventPublisher; import org.apache.hadoop.hdds.server.events.EventQueue; +import org.apache.hadoop.ozone.OzoneConsts; import org.apache.hadoop.security.authentication.client .AuthenticationException; import org.apache.hadoop.test.GenericTestUtils; @@ -131,7 +132,8 @@ public void testOnMessage() throws Exception { .concat("/" + datanode1.getUuidString()); StorageReportProto storageOne = TestUtils.createStorageReport( - datanode1.getUuid(), storagePath, 100, 10, 90, null); + datanode1.getUuid(), storagePath, 100 * OzoneConsts.TB, + 10 * OzoneConsts.TB, 90 * OzoneConsts.TB, null); // Exit safemode, as otherwise the safemode precheck will prevent pipelines // from getting created. Due to how this test is wired up, safemode will diff --git a/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/pipeline/TestPipelinePlacementPolicy.java b/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/pipeline/TestPipelinePlacementPolicy.java index a724ad3bdb20..deecebf35421 100644 --- a/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/pipeline/TestPipelinePlacementPolicy.java +++ b/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/pipeline/TestPipelinePlacementPolicy.java @@ -58,6 +58,7 @@ import static junit.framework.TestCase.assertEquals; import static junit.framework.TestCase.assertTrue; import static org.apache.hadoop.hdds.scm.ScmConfigKeys.OZONE_DATANODE_PIPELINE_LIMIT; +import static org.apache.hadoop.hdds.scm.ScmConfigKeys.OZONE_SCM_CONTAINER_SIZE; import static org.apache.hadoop.hdds.scm.net.NetConstants.LEAF_SCHEMA; import static org.apache.hadoop.hdds.scm.net.NetConstants.RACK_SCHEMA; import static org.apache.hadoop.hdds.scm.net.NetConstants.ROOT_SCHEMA; @@ -174,6 +175,30 @@ public void testChooseNodeWithSingleNodeRack() throws SCMException { Assert.assertNotEquals(results.get(1).getNetworkLocation(), results.get(2).getNetworkLocation()); } + + @Test + public void testChooseNodeNotEnoughSpace() throws SCMException { + // A huge container size + conf.set(OZONE_SCM_CONTAINER_SIZE, "10TB"); + + // There is only one node on 3 racks altogether. + List datanodes = new ArrayList<>(); + for (Node node : SINGLE_NODE_RACK) { + DatanodeDetails datanode = overwriteLocationInNode( + MockDatanodeDetails.randomDatanodeDetails(), node); + datanodes.add(datanode); + } + MockNodeManager localNodeManager = new MockNodeManager(initTopology(), + datanodes, false, datanodes.size()); + PipelinePlacementPolicy localPlacementPolicy = new PipelinePlacementPolicy( + localNodeManager, new PipelineStateManager(), conf); + int nodesRequired = HddsProtos.ReplicationFactor.THREE.getNumber(); + + thrownExp.expect(SCMException.class); + thrownExp.expectMessage("enough space for even a single container"); + localPlacementPolicy.chooseDatanodes(new ArrayList<>(datanodes.size()), + new ArrayList<>(datanodes.size()), nodesRequired, 0); + } @Test public void testPickLowestLoadAnchor() throws IOException{