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{