/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.hadoop.yarn.server.resourcemanager.scheduler.capacity; import static org.junit.Assert.assertEquals; import static org.mockito.Matchers.any; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import java.util.HashMap; import java.util.Map; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.hadoop.yarn.api.records.ApplicationAttemptId; import org.apache.hadoop.yarn.api.records.Container; import org.apache.hadoop.yarn.api.records.ContainerId; import org.apache.hadoop.yarn.api.records.ContainerStatus; import org.apache.hadoop.yarn.api.records.Priority; import org.apache.hadoop.yarn.api.records.Resource; import org.apache.hadoop.yarn.conf.YarnConfiguration; import org.apache.hadoop.yarn.event.DrainDispatcher; import org.apache.hadoop.yarn.event.EventHandler; import org.apache.hadoop.yarn.server.resourcemanager.RMContext; import org.apache.hadoop.yarn.server.resourcemanager.rmcontainer.ContainerAllocationExpirer; import org.apache.hadoop.yarn.server.resourcemanager.rmcontainer.RMContainer; import org.apache.hadoop.yarn.server.resourcemanager.rmcontainer.RMContainerEventType; import org.apache.hadoop.yarn.server.resourcemanager.rmcontainer.RMContainerImpl; import org.apache.hadoop.yarn.server.resourcemanager.scheduler.NodeType; import org.apache.hadoop.yarn.server.resourcemanager.scheduler.common.fica.FiCaSchedulerApp; import org.apache.hadoop.yarn.server.resourcemanager.scheduler.common.fica.FiCaSchedulerNode; import org.apache.hadoop.yarn.server.utils.BuilderUtils; import org.apache.hadoop.yarn.util.resource.DefaultResourceCalculator; import org.apache.hadoop.yarn.util.resource.ResourceCalculator; import org.apache.hadoop.yarn.util.resource.Resources; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.mockito.InOrder; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; public class TestChildQueueOrder { private static final Log LOG = LogFactory.getLog(TestChildQueueOrder.class); RMContext rmContext; YarnConfiguration conf; CapacitySchedulerConfiguration csConf; CapacitySchedulerContext csContext; final static int GB = 1024; final static String DEFAULT_RACK = "/default"; private final ResourceCalculator resourceComparator = new DefaultResourceCalculator(); @Before public void setUp() throws Exception { rmContext = TestUtils.getMockRMContext(); conf = new YarnConfiguration(); csConf = new CapacitySchedulerConfiguration(); csContext = mock(CapacitySchedulerContext.class); when(csContext.getConf()).thenReturn(conf); when(csContext.getConfiguration()).thenReturn(csConf); when(csContext.getMinimumResourceCapability()).thenReturn( Resources.createResource(GB, 1)); when(csContext.getMaximumResourceCapability()).thenReturn( Resources.createResource(16*GB, 32)); when(csContext.getClusterResources()). thenReturn(Resources.createResource(100 * 16 * GB, 100 * 32)); when(csContext.getApplicationComparator()). thenReturn(CapacityScheduler.applicationComparator); when(csContext.getQueueComparator()). thenReturn(CapacityScheduler.queueComparator); when(csContext.getResourceCalculator()). thenReturn(resourceComparator); } private FiCaSchedulerApp getMockApplication(int appId, String user) { FiCaSchedulerApp application = mock(FiCaSchedulerApp.class); doReturn(user).when(application).getUser(); doReturn(Resources.createResource(0, 0)).when(application).getHeadroom(); return application; } private void stubQueueAllocation(final CSQueue queue, final Resource clusterResource, final FiCaSchedulerNode node, final int allocation) { stubQueueAllocation(queue, clusterResource, node, allocation, NodeType.NODE_LOCAL); } private void stubQueueAllocation(final CSQueue queue, final Resource clusterResource, final FiCaSchedulerNode node, final int allocation, final NodeType type) { // Simulate the queue allocation doAnswer(new Answer<CSAssignment>() { @Override public CSAssignment answer(InvocationOnMock invocation) throws Throwable { try { throw new Exception(); } catch (Exception e) { LOG.info("FOOBAR q.assignContainers q=" + queue.getQueueName() + " alloc=" + allocation + " node=" + node.getNodeName()); } final Resource allocatedResource = Resources.createResource(allocation); if (queue instanceof ParentQueue) { ((ParentQueue)queue).allocateResource(clusterResource, allocatedResource); } else { FiCaSchedulerApp app1 = getMockApplication(0, ""); ((LeafQueue)queue).allocateResource(clusterResource, app1, allocatedResource); } // Next call - nothing if (allocation > 0) { doReturn(new CSAssignment(Resources.none(), type)). when(queue).assignContainers(eq(clusterResource), eq(node)); // Mock the node's resource availability Resource available = node.getAvailableResource(); doReturn(Resources.subtractFrom(available, allocatedResource)). when(node).getAvailableResource(); } return new CSAssignment(allocatedResource, type); } }). when(queue).assignContainers(eq(clusterResource), eq(node)); doNothing().when(node).releaseContainer(any(Container.class)); } private float computeQueueAbsoluteUsedCapacity(CSQueue queue, int expectedMemory, Resource clusterResource) { return ( ((float)expectedMemory / (float)clusterResource.getMemory()) ); } private float computeQueueUsedCapacity(CSQueue queue, int expectedMemory, Resource clusterResource) { return (expectedMemory / (clusterResource.getMemory() * queue.getAbsoluteCapacity())); } final static float DELTA = 0.0001f; private void verifyQueueMetrics(CSQueue queue, int expectedMemory, Resource clusterResource) { assertEquals( computeQueueAbsoluteUsedCapacity(queue, expectedMemory, clusterResource), queue.getAbsoluteUsedCapacity(), DELTA); assertEquals( computeQueueUsedCapacity(queue, expectedMemory, clusterResource), queue.getUsedCapacity(), DELTA); } private static final String A = "a"; private static final String B = "b"; private static final String C = "c"; private static final String D = "d"; private void setupSortedQueues(CapacitySchedulerConfiguration conf) { // Define queues csConf.setQueues(CapacitySchedulerConfiguration.ROOT, new String[] {A, B, C, D}); final String Q_A = CapacitySchedulerConfiguration.ROOT + "." + A; conf.setCapacity(Q_A, 25); final String Q_B = CapacitySchedulerConfiguration.ROOT + "." + B; conf.setCapacity(Q_B, 25); final String Q_C = CapacitySchedulerConfiguration.ROOT + "." + C; conf.setCapacity(Q_C, 25); final String Q_D = CapacitySchedulerConfiguration.ROOT + "." + D; conf.setCapacity(Q_D, 25); } @Test public void testSortedQueues() throws Exception { // Setup queue configs setupSortedQueues(csConf); Map<String, CSQueue> queues = new HashMap<String, CSQueue>(); CSQueue root = CapacityScheduler.parseQueue(csContext, csConf, null, CapacitySchedulerConfiguration.ROOT, queues, queues, TestUtils.spyHook); // Setup some nodes final int memoryPerNode = 10; final int coresPerNode = 16; final int numNodes = 1; FiCaSchedulerNode node_0 = TestUtils.getMockNode("host_0", DEFAULT_RACK, 0, memoryPerNode*GB); doNothing().when(node_0).releaseContainer(any(Container.class)); final Resource clusterResource = Resources.createResource(numNodes * (memoryPerNode*GB), numNodes * coresPerNode); when(csContext.getNumClusterNodes()).thenReturn(numNodes); // Start testing CSQueue a = queues.get(A); CSQueue b = queues.get(B); CSQueue c = queues.get(C); CSQueue d = queues.get(D); final String user_0 = "user_0"; // Stub an App and its containerCompleted FiCaSchedulerApp app_0 = getMockApplication(0,user_0); doReturn(true).when(app_0).containerCompleted(any(RMContainer.class), any(ContainerStatus.class),any(RMContainerEventType.class)); // Priority priority = TestUtils.createMockPriority(1); ContainerAllocationExpirer expirer = mock(ContainerAllocationExpirer.class); DrainDispatcher drainDispatcher = new DrainDispatcher(); EventHandler eventHandler = drainDispatcher.getEventHandler(); ApplicationAttemptId appAttemptId = BuilderUtils.newApplicationAttemptId( app_0.getApplicationId(), 1); ContainerId containerId = BuilderUtils.newContainerId(appAttemptId, 1); Container container=TestUtils.getMockContainer(containerId, node_0.getNodeID(), Resources.createResource(1*GB), priority); RMContainer rmContainer = new RMContainerImpl(container, appAttemptId, node_0.getNodeID(), eventHandler, expirer); // Assign {1,2,3,4} 1GB containers respectively to queues stubQueueAllocation(a, clusterResource, node_0, 1*GB); stubQueueAllocation(b, clusterResource, node_0, 0*GB); stubQueueAllocation(c, clusterResource, node_0, 0*GB); stubQueueAllocation(d, clusterResource, node_0, 0*GB); root.assignContainers(clusterResource, node_0); for(int i=0; i < 2; i++) { stubQueueAllocation(a, clusterResource, node_0, 0*GB); stubQueueAllocation(b, clusterResource, node_0, 1*GB); stubQueueAllocation(c, clusterResource, node_0, 0*GB); stubQueueAllocation(d, clusterResource, node_0, 0*GB); root.assignContainers(clusterResource, node_0); } for(int i=0; i < 3; i++) { stubQueueAllocation(a, clusterResource, node_0, 0*GB); stubQueueAllocation(b, clusterResource, node_0, 0*GB); stubQueueAllocation(c, clusterResource, node_0, 1*GB); stubQueueAllocation(d, clusterResource, node_0, 0*GB); root.assignContainers(clusterResource, node_0); } for(int i=0; i < 4; i++) { stubQueueAllocation(a, clusterResource, node_0, 0*GB); stubQueueAllocation(b, clusterResource, node_0, 0*GB); stubQueueAllocation(c, clusterResource, node_0, 0*GB); stubQueueAllocation(d, clusterResource, node_0, 1*GB); root.assignContainers(clusterResource, node_0); } verifyQueueMetrics(a, 1*GB, clusterResource); verifyQueueMetrics(b, 2*GB, clusterResource); verifyQueueMetrics(c, 3*GB, clusterResource); verifyQueueMetrics(d, 4*GB, clusterResource); LOG.info("status child-queues: " + ((ParentQueue)root). getChildQueuesToPrint()); //Release 3 x 1GB containers from D for(int i=0; i < 3;i++) { d.completedContainer(clusterResource, app_0, node_0, rmContainer, null, RMContainerEventType.KILL, null); } verifyQueueMetrics(a, 1*GB, clusterResource); verifyQueueMetrics(b, 2*GB, clusterResource); verifyQueueMetrics(c, 3*GB, clusterResource); verifyQueueMetrics(d, 1*GB, clusterResource); //reset manually resources on node node_0 = TestUtils.getMockNode("host_0", DEFAULT_RACK, 0, (memoryPerNode-1-2-3-1)*GB); LOG.info("status child-queues: " + ((ParentQueue)root).getChildQueuesToPrint()); // Assign 2 x 1GB Containers to A for(int i=0; i < 2; i++) { stubQueueAllocation(a, clusterResource, node_0, 1*GB); stubQueueAllocation(b, clusterResource, node_0, 0*GB); stubQueueAllocation(c, clusterResource, node_0, 0*GB); stubQueueAllocation(d, clusterResource, node_0, 0*GB); root.assignContainers(clusterResource, node_0); } verifyQueueMetrics(a, 3*GB, clusterResource); verifyQueueMetrics(b, 2*GB, clusterResource); verifyQueueMetrics(c, 3*GB, clusterResource); verifyQueueMetrics(d, 1*GB, clusterResource); LOG.info("status child-queues: " + ((ParentQueue)root).getChildQueuesToPrint()); //Release 1GB Container from A a.completedContainer(clusterResource, app_0, node_0, rmContainer, null, RMContainerEventType.KILL, null); verifyQueueMetrics(a, 2*GB, clusterResource); verifyQueueMetrics(b, 2*GB, clusterResource); verifyQueueMetrics(c, 3*GB, clusterResource); verifyQueueMetrics(d, 1*GB, clusterResource); //reset manually resources on node node_0 = TestUtils.getMockNode("host_0", DEFAULT_RACK, 0, (memoryPerNode-2-2-3-1)*GB); LOG.info("status child-queues: " + ((ParentQueue)root).getChildQueuesToPrint()); // Assign 1GB container to B stubQueueAllocation(a, clusterResource, node_0, 0*GB); stubQueueAllocation(b, clusterResource, node_0, 1*GB); stubQueueAllocation(c, clusterResource, node_0, 0*GB); stubQueueAllocation(d, clusterResource, node_0, 0*GB); root.assignContainers(clusterResource, node_0); verifyQueueMetrics(a, 2*GB, clusterResource); verifyQueueMetrics(b, 3*GB, clusterResource); verifyQueueMetrics(c, 3*GB, clusterResource); verifyQueueMetrics(d, 1*GB, clusterResource); LOG.info("status child-queues: " + ((ParentQueue)root).getChildQueuesToPrint()); //Release 1GB container resources from B b.completedContainer(clusterResource, app_0, node_0, rmContainer, null, RMContainerEventType.KILL, null); verifyQueueMetrics(a, 2*GB, clusterResource); verifyQueueMetrics(b, 2*GB, clusterResource); verifyQueueMetrics(c, 3*GB, clusterResource); verifyQueueMetrics(d, 1*GB, clusterResource); //reset manually resources on node node_0 = TestUtils.getMockNode("host_0", DEFAULT_RACK, 0, (memoryPerNode-2-2-3-1)*GB); LOG.info("status child-queues: " + ((ParentQueue)root).getChildQueuesToPrint()); // Assign 1GB container to A stubQueueAllocation(a, clusterResource, node_0, 1*GB); stubQueueAllocation(b, clusterResource, node_0, 0*GB); stubQueueAllocation(c, clusterResource, node_0, 0*GB); stubQueueAllocation(d, clusterResource, node_0, 0*GB); root.assignContainers(clusterResource, node_0); verifyQueueMetrics(a, 3*GB, clusterResource); verifyQueueMetrics(b, 2*GB, clusterResource); verifyQueueMetrics(c, 3*GB, clusterResource); verifyQueueMetrics(d, 1*GB, clusterResource); LOG.info("status child-queues: " + ((ParentQueue)root).getChildQueuesToPrint()); // Now do the real test, where B and D request a 1GB container // D should should get the next container if the order is correct stubQueueAllocation(a, clusterResource, node_0, 0*GB); stubQueueAllocation(b, clusterResource, node_0, 1*GB); stubQueueAllocation(c, clusterResource, node_0, 0*GB); stubQueueAllocation(d, clusterResource, node_0, 1*GB); root.assignContainers(clusterResource, node_0); InOrder allocationOrder = inOrder(d,b); allocationOrder.verify(d).assignContainers(eq(clusterResource), any(FiCaSchedulerNode.class)); allocationOrder.verify(b).assignContainers(eq(clusterResource), any(FiCaSchedulerNode.class)); verifyQueueMetrics(a, 3*GB, clusterResource); verifyQueueMetrics(b, 2*GB, clusterResource); verifyQueueMetrics(c, 3*GB, clusterResource); verifyQueueMetrics(d, 2*GB, clusterResource); //D got the container LOG.info("status child-queues: " + ((ParentQueue)root).getChildQueuesToPrint()); } @After public void tearDown() throws Exception { } }