diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/util/resource/Resources.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/util/resource/Resources.java index 1c08844cf33..03006553bad 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/util/resource/Resources.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/util/resource/Resources.java @@ -493,6 +493,31 @@ public static boolean fitsIn(Resource smaller, Resource bigger) { } return true; } + + public static boolean hasAnyZeroRequestedResource(Resource requested, + Resource available) { + int maxLength = ResourceUtils.getNumberOfKnownResourceTypes(); + for (int i = 0; i < maxLength; i++) { + try { + ResourceInformation availableValue = + available.getResourceInformation(i); + ResourceInformation requestedValue = + requested.getResourceInformation(i); + + long convertedAvailable = + (availableValue.getUnits().equals(requestedValue.getUnits())) + ? availableValue.getValue() + : UnitsConversionUtil.convert(availableValue.getUnits(), + requestedValue.getUnits(), availableValue.getValue()); + if (requestedValue.getValue() > 0 && convertedAvailable == 0) { + return true; + } + } catch (ResourceNotFoundException ye) { + LOG.warn("Resource is missing:" + ye.getMessage()); + } + } + return false; + } public static boolean fitsIn(ResourceCalculator rc, Resource smaller, Resource bigger) { diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/test/java/org/apache/hadoop/yarn/util/resource/TestResources.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/test/java/org/apache/hadoop/yarn/util/resource/TestResources.java index a8404fbaee7..45b1a35ae3e 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/test/java/org/apache/hadoop/yarn/util/resource/TestResources.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/test/java/org/apache/hadoop/yarn/util/resource/TestResources.java @@ -31,6 +31,8 @@ import static org.apache.hadoop.yarn.util.resource.Resources.componentwiseMin; import static org.apache.hadoop.yarn.util.resource.Resources.componentwiseMax; import static org.apache.hadoop.yarn.util.resource.Resources.add; +import static org.apache.hadoop.yarn.util.resource.Resources + .hasAnyZeroRequestedResource; import static org.apache.hadoop.yarn.util.resource.Resources.subtract; import static org.apache.hadoop.yarn.util.resource.Resources.multiply; import static org.apache.hadoop.yarn.util.resource.Resources.multiplyAndAddTo; @@ -263,4 +265,18 @@ public void testMultiplyAndAddTo() throws Exception { multiplyAndAddTo(createResource(3, 1, 2), createResource(2, 2, 3), 1.5)); } + + @Test + public void testHasAnyZeroRequestedResource() { + Resource requestedResource = createResource(1, 0); + Resource resourceWithZeroMemory = createResource(0, 4); + assertTrue(hasAnyZeroRequestedResource(requestedResource, resourceWithZeroMemory)); + } + + @Test + public void testHasAnyZeroRequestedResource2() { + Resource requestedResource = createResource(1, 3); + Resource resourceWithZeroMemory = createResource(1, 4); + assertFalse(hasAnyZeroRequestedResource(requestedResource, resourceWithZeroMemory)); + } } diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/fair/FSAppAttempt.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/fair/FSAppAttempt.java index 0305702fb5f..98890765590 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/fair/FSAppAttempt.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/fair/FSAppAttempt.java @@ -1096,7 +1096,8 @@ private boolean hasContainerForNode(SchedulerRequestKey key, } else if (!getQueue().fitsInMaxShare(resource)) { // The requested container must fit in queue maximum share updateAMDiagnosticMsg(resource, - " exceeds current queue or its parents maximum resource allowed)."); + " exceeds current queue or its parents maximum resource allowed). " + + "Max share of queue: " + getQueue().getMaxShare()); ret = false; } diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/fair/FairScheduler.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/fair/FairScheduler.java index 1f85814adac..1d5318988e3 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/fair/FairScheduler.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/fair/FairScheduler.java @@ -476,6 +476,35 @@ protected void addApplication(ApplicationId applicationId, return; } + if (rmApp == null || rmApp.getAMResourceRequests() == null) { + LOG.debug("rmApp or rmApp.AMResourceRequests was null!"); + } + + if (rmApp != null && rmApp.getAMResourceRequests() != null) { + for (ResourceRequest amResourceRequest : rmApp + .getAMResourceRequests()) { + LOG.debug("amResourceRequest of rmApp " + applicationId + ": " + + amResourceRequest.getCapability()); + LOG.debug("Max share of queue " + queue.getName() + ": " + + queue.getMaxShare()); + + if (Resources.hasAnyZeroRequestedResource( + amResourceRequest.getCapability(), queue.getMaxShare())) { + String msg = String.format( + "Cannot submit application %s " + "to queue %s because " + + "it has zero amount of resource for a requested " + + "resource! " + "Requested AM resource: %s, " + + "maximum queue resources: %s", + applicationId, queue.getName(), + amResourceRequest.getCapability(), queue.getMaxShare()); + LOG.info(msg); + rmContext.getDispatcher().getEventHandler().handle(new RMAppEvent( + applicationId, RMAppEventType.APP_REJECTED, msg)); + return; + } + } + } + // Enforce ACLs UserGroupInformation userUgi = UserGroupInformation.createRemoteUser( user); diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/fair/FairSchedulerTestBase.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/fair/FairSchedulerTestBase.java index b99856467cf..b00b64a2abc 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/fair/FairSchedulerTestBase.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/fair/FairSchedulerTestBase.java @@ -252,13 +252,36 @@ protected void createSchedulingRequestExistingApplication( protected void createApplicationWithAMResource(ApplicationAttemptId attId, String queue, String user, Resource amResource) { + createApplicationWithAMResourceInternal(attId, queue, user, amResource, + null); + ApplicationId appId = attId.getApplicationId(); + processEvents(queue, user, appId); + processAttempAddedEvent(attId); + } + + protected void createApplicationWithAMResource(ApplicationAttemptId attId, + String queue, String user, Resource amResource, + List amReqs) { + createApplicationWithAMResourceInternal(attId, queue, user, amResource, + amReqs); + ApplicationId appId = attId.getApplicationId(); + processEvents(queue, user, appId); + } + + private void createApplicationWithAMResourceInternal( + ApplicationAttemptId attId, String queue, String user, + Resource amResource, List amReqs) { RMContext rmContext = resourceManager.getRMContext(); ApplicationId appId = attId.getApplicationId(); RMApp rmApp = new RMAppImpl(appId, rmContext, conf, null, user, null, ApplicationSubmissionContext.newInstance(appId, null, queue, null, mock(ContainerLaunchContext.class), false, false, 0, amResource, - null), scheduler, null, 0, null, null, null); + null), + scheduler, null, 0, null, null, amReqs); rmContext.getRMApps().put(appId, rmApp); + } + + private void processEvents(String queue, String user, ApplicationId appId) { RMAppEvent event = new RMAppEvent(appId, RMAppEventType.START); resourceManager.getRMContext().getRMApps().get(appId).handle(event); event = new RMAppEvent(appId, RMAppEventType.APP_NEW_SAVED); @@ -268,8 +291,11 @@ protected void createApplicationWithAMResource(ApplicationAttemptId attId, AppAddedSchedulerEvent appAddedEvent = new AppAddedSchedulerEvent( appId, queue, user); scheduler.handle(appAddedEvent); + } + + private void processAttempAddedEvent(ApplicationAttemptId attId) { AppAttemptAddedSchedulerEvent attempAddedEvent = - new AppAttemptAddedSchedulerEvent(attId, false); + new AppAttemptAddedSchedulerEvent(attId, false); scheduler.handle(attempAddedEvent); } diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/fair/TestFairScheduler.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/fair/TestFairScheduler.java index d9c06a79db2..49b70c0e195 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/fair/TestFairScheduler.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/fair/TestFairScheduler.java @@ -44,6 +44,7 @@ import javax.xml.parsers.ParserConfigurationException; +import com.google.common.collect.Lists; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.CommonConfigurationKeys; import org.apache.hadoop.ha.HAServiceProtocol; @@ -62,6 +63,7 @@ import org.apache.hadoop.yarn.api.records.NodeState; import org.apache.hadoop.yarn.api.records.QueueInfo; import org.apache.hadoop.yarn.api.records.Resource; +import org.apache.hadoop.yarn.api.records.ResourceInformation; import org.apache.hadoop.yarn.api.records.ResourceRequest; import org.apache.hadoop.yarn.api.records.impl.pb.ApplicationSubmissionContextPBImpl; import org.apache.hadoop.yarn.conf.YarnConfiguration; @@ -5414,4 +5416,82 @@ public void testCompletedContainerOnRemovedNode() throws IOException { SchedulerUtils.COMPLETED_APPLICATION), RMContainerEventType.EXPIRE); } + + @Test + public void testAppRejectedOnQueueZeroCapacityOfResourceVcores() + throws IOException { + testAppRejectedOnQueueZeroCapacityOfResource( + ResourceInformation.VCORES_URI); + } + + @Test + public void testAppRejectedOnQueueZeroCapacityOfResourceMemory() + throws IOException { + testAppRejectedOnQueueZeroCapacityOfResource( + ResourceInformation.MEMORY_URI); + } + + private void testAppRejectedOnQueueZeroCapacityOfResource(String resource) + throws IOException { + conf.set(FairSchedulerConfiguration.ALLOCATION_FILE, ALLOC_FILE); + PrintWriter out = new PrintWriter(new FileWriter(ALLOC_FILE)); + out.println(""); + out.println(""); + out.println(""); + + String resources = ""; + if (resource.equals(ResourceInformation.MEMORY_URI)) { + resources = "0 mb,2vcores"; + } else if (resource.equals(ResourceInformation.VCORES_URI)) { + resources = "10000 mb,0vcores"; + } + out.println("" + resources + ""); + out.println("" + resources + ""); + out.println("2.0"); + out.println(""); + out.println(""); + out.println("1 mb 1 vcores"); + out.println("0.0"); + out.println(""); + out.println(""); + out.close(); + + final List recordedEvents = Lists.newArrayList(); + + RMContext spyContext = Mockito.spy(resourceManager.getRMContext()); + Dispatcher mockDispatcher = mock(AsyncDispatcher.class); + when(mockDispatcher.getEventHandler()).thenReturn((EventHandler) event -> { + if (event instanceof RMAppEvent) { + recordedEvents.add(event); + } + }); + Mockito.doReturn(mockDispatcher).when(spyContext).getDispatcher(); + ((AsyncDispatcher) mockDispatcher).start(); + + scheduler.setRMContext(spyContext); + + scheduler.init(conf); + scheduler.start(); + scheduler.reinitialize(conf, resourceManager.getRMContext()); + + // submit app with queue name (queueA) + ApplicationAttemptId appAttemptId1 = createAppAttemptId(1, 1); + + ResourceRequest amReqs = ResourceRequest.newBuilder() + .capability(Resource.newInstance(5 * GB, 3)).build(); + createApplicationWithAMResource(appAttemptId1, "queueA", "user1", + Resource.newInstance(GB, 1), Lists.newArrayList(amReqs)); + scheduler.update(); + + assertEquals("Exactly one APP_REJECTED event is expected", 1, + recordedEvents.size()); + Event event = recordedEvents.get(0); + RMAppEvent rmAppEvent = (RMAppEvent) event; + assertEquals(RMAppEventType.APP_REJECTED, rmAppEvent.getType()); + assertTrue(rmAppEvent.getDiagnosticMsg() + .matches("Cannot submit application application[\\d_]+ to queue " + + "root.queueA because it has zero amount of resource " + + "for a requested resource! " + "Requested AM resource: .+, " + + "maximum queue resources: .+")); + } }