/** * 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.monitor.capacity; import org.apache.hadoop.yarn.server.resourcemanager.monitor.capacity.TestProportionalCapacityPreemptionPolicy.IsPreemptionRequestFor; import org.junit.Before; import org.junit.Test; import java.io.IOException; import static org.mockito.Matchers.argThat; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; public class TestProportionalCapacityPreemptionPolicyForNodePartitions extends ProportionalCapacityPreemptionPolicyMockFramework { @Before public void setup() { super.setup(); policy = new ProportionalCapacityPreemptionPolicy(rmContext, cs, mClock); } @Test public void testNodePartitionPreemptionRespectGuaranteedCapacity() throws IOException { /** * The simplest test of node label, Queue structure is: * * <pre> * root * / \ * a b * </pre> * * Both a/b can access x, and guaranteed capacity of them is 50:50. Two * nodes, n1 has 100 x, n2 has 100 NO_LABEL 4 applications in the cluster, * app1/app2 in a, and app3/app4 in b. * app1 uses 80 x, app2 uses 20 NO_LABEL, app3 uses 20 x, app4 uses 80 NO_LABEL. * Both a/b have 50 pending resource for x and NO_LABEL * * After preemption, it should preempt 30 from app1, and 30 from app4. */ String labelsConfig = "=100,true;" + // default partition "x=100,true"; // partition=x String nodesConfig = "n1=x;" + // n1 has partition=x "n2="; // n2 is default partition String queuesConfig = // guaranteed,max,used,pending "root(=[100 100 100 100],x=[100 100 100 100]);" + //root "-a(=[50 100 20 50],x=[50 100 80 50]);" + // a "-b(=[50 100 80 50],x=[50 100 20 50])"; // b String appsConfig= //queueName\t(priority,resource,host,expression,#repeat,reserved) "a\t" // app1 in a + "(1,1,n1,x,80,false);" + // 80 * x in n1 "a\t" // app2 in a + "(1,1,n2,,20,false);" + // 20 default in n2 "b\t" // app3 in b + "(1,1,n1,x,20,false);" + // 80 * x in n1 "b\t" // app4 in b + "(1,1,n2,,80,false)"; // 20 default in n2 buildEnv(labelsConfig, nodesConfig, queuesConfig, appsConfig); policy.editSchedule(); // 30 preempted from app1, 30 preempted from app4, and nothing preempted // from app2/app3 verify(mDisp, times(30)).handle( argThat(new IsPreemptionRequestFor(getAppAttemptId(1)))); verify(mDisp, times(30)).handle( argThat(new IsPreemptionRequestFor(getAppAttemptId(4)))); verify(mDisp, never()).handle( argThat(new IsPreemptionRequestFor(getAppAttemptId(2)))); verify(mDisp, never()).handle( argThat(new IsPreemptionRequestFor(getAppAttemptId(3)))); } @Test public void testNodePartitionPreemptionNotHappenBetweenSatisfiedQueues() throws IOException { /** * Queue structure is: * * <pre> * root * / | \ * a b c * </pre> * * Both a/b/c can access x, and guaranteed_capacity(x) of them is 80:10:10. * a/b's max resource is 100, and c's max resource is 30. * * Two nodes, n1 has 100 x, n2 has 100 NO_LABEL. * * 2 apps in cluster. * app1 in b and app2 in c. * * app1 uses 90x, and app2 use 10x. We don't expect preemption happen * between them because all of them are satisfied */ String labelsConfig = "=100,true;" + // default partition "x=100,true"; // partition=x String nodesConfig = "n1=x;" + // n1 has partition=x "n2="; // n2 is default partition String queuesConfig = // guaranteed,max,used,pending "root(=[100 100 100 100],x=[100 100 100 100]);" + //root "-a(=[80 80 0 0],x=[80 80 0 0]);" + // a "-b(=[10 100 0 0],x=[10 100 90 50]);" + // b "-c(=[10 100 0 0],x=[10 30 10 50])"; //c String appsConfig= //queueName\t(priority,resource,host,expression,#repeat,reserved) "b\t" // app1 in b + "(1,1,n1,x,90,false);" + // 80 * x in n1 "c\t" // app2 in c + "(1,1,n1,x,10,false)"; // 20 default in n2 buildEnv(labelsConfig, nodesConfig, queuesConfig, appsConfig); policy.editSchedule(); // No preemption happens verify(mDisp, never()).handle( argThat(new IsPreemptionRequestFor(getAppAttemptId(1)))); verify(mDisp, never()).handle( argThat(new IsPreemptionRequestFor(getAppAttemptId(2)))); } @Test public void testNodePartitionPreemptionOfIgnoreExclusivityAndRespectCapacity() throws IOException { /** * <pre> * root * / \ * a b * </pre> * * Both a/b can access x, and guaranteed capacity of them is 50:50. Two * nodes, n1 has 100 x, n2 has 100 NO_LABEL and 2 applications in the cluster, * app1/app2 in a * app1 uses 20x (ignoreExclusivity), app2 uses 80x (respectExclusivity). * * b has 100 pending resource of x * * After preemption, it should preempt 20 from app1, and 30 from app2. */ String labelsConfig = "=100,true;" + // default partition "x=100,false"; // partition=x String nodesConfig = "n1=x;" + // n1 has partition=x "n2="; // n2 is default partition String queuesConfig = // guaranteed,max,used,pending "root(=[100 100 100 100],x=[100 100 100 100]);" + //root "-a(=[50 100 0 0],x=[50 100 100 50]);" + // a "-b(=[50 100 0 0],x=[50 100 0 100])"; // b String appsConfig= //queueName\t(priority,resource,host,expression,#repeat,reserved) "a\t" // app1 in a + "(1,1,n1,x,1,false)" // 1 * x in n1 (it's AM container) + "(1,1,n1,,20,false);" + // 20 * x in n1 (ignoreExclusivity) "a\t" // app2 in a + "(1,1,n1,x,79,false)"; // 79 * x buildEnv(labelsConfig, nodesConfig, queuesConfig, appsConfig); policy.editSchedule(); // 30 preempted from app1, 30 preempted from app4, and nothing preempted // from app2/app3 verify(mDisp, times(20)).handle( argThat(new IsPreemptionRequestFor(getAppAttemptId(1)))); verify(mDisp, times(30)).handle( argThat(new IsPreemptionRequestFor(getAppAttemptId(2)))); } @Test public void testNodePartitionPreemptionOfSkippingAMContainer() throws IOException { /** * <pre> * root * / \ * a b * </pre> * * Both a/b can access x, and guaranteed capacity of them is 20:80. Two * nodes, n1 has 100 x, n2 has 100 NO_LABEL and 2 applications in the cluster, * app1/app2/app3/app4/app5 in a, both uses 20 resources. * * b has 100 pending resource of x * * After preemption, it should preempt 19 from app[5-2] an 4 from app1 */ String labelsConfig = "=100,true;" + // default partition "x=100,true"; // partition=x String nodesConfig = "n1=x;" + // n1 has partition=x "n2="; // n2 is default partition String queuesConfig = // guaranteed,max,used,pending "root(=[100 100 100 100],x=[100 100 100 100]);" + //root "-a(=[50 100 0 0],x=[20 100 100 50]);" + // a "-b(=[50 100 0 0],x=[80 100 0 100])"; // b String appsConfig= //queueName\t(priority,resource,host,expression,#repeat,reserved) "a\t" // app1 in a + "(1,1,n1,x,20,false);" + // uses 20 resource "a\t" // app2 in a + "(1,1,n1,x,20,false);" + // uses 20 resource "a\t" // app3 in a + "(1,1,n1,x,20,false);" + // uses 20 resource "a\t" // app4 in a + "(1,1,n1,x,20,false);" + // uses 20 resource "a\t" // app5 in a + "(1,1,n1,x,20,false);"; // uses 20 resource buildEnv(labelsConfig, nodesConfig, queuesConfig, appsConfig); policy.editSchedule(); // 4 from app1 verify(mDisp, times(4)).handle( argThat(new IsPreemptionRequestFor(getAppAttemptId(1)))); // 19 from app2-app5 verify(mDisp, times(19)).handle( argThat(new IsPreemptionRequestFor(getAppAttemptId(2)))); verify(mDisp, times(19)).handle( argThat(new IsPreemptionRequestFor(getAppAttemptId(3)))); verify(mDisp, times(19)).handle( argThat(new IsPreemptionRequestFor(getAppAttemptId(4)))); verify(mDisp, times(19)).handle( argThat(new IsPreemptionRequestFor(getAppAttemptId(5)))); } @Test public void testNodePartitionPreemptionOfAMContainer() throws IOException { /** * <pre> * root * / \ * a b * </pre> * * Both a/b can access x, and guaranteed capacity of them is 3:97. Two * nodes, n1 has 100 x, n2 has 100 NO_LABEL. * * app1/app2/app3/app4/app5 in a, both uses 20 resources(x) * * b has 100 pending resource of x * * After preemption, it should preempt 20 from app4/app5 an 19 from * app1-app3. App4/app5's AM container will be preempted */ String labelsConfig = "=100,true;" + // default partition "x=100,true"; // partition=x String nodesConfig = "n1=x;" + // n1 has partition=x "n2="; // n2 is default partition String queuesConfig = // guaranteed,max,used,pending "root(=[100 100 100 100],x=[100 100 100 100]);" + //root "-a(=[50 100 0 0],x=[3 100 100 50]);" + // a "-b(=[50 100 0 0],x=[97 100 0 100])"; // b String appsConfig= //queueName\t(priority,resource,host,expression,#repeat,reserved) "a\t" // app1 in a + "(1,1,n1,x,20,false);" + // uses 20 resource "a\t" // app2 in a + "(1,1,n1,x,20,false);" + // uses 20 resource "a\t" // app3 in a + "(1,1,n1,x,20,false);" + // uses 20 resource "a\t" // app4 in a + "(1,1,n1,x,20,false);" + // uses 20 resource "a\t" // app5 in a + "(1,1,n1,x,20,false);"; // uses 20 resource buildEnv(labelsConfig, nodesConfig, queuesConfig, appsConfig); policy.editSchedule(); // 4 from app1 verify(mDisp, times(19)).handle( argThat(new IsPreemptionRequestFor(getAppAttemptId(1)))); // 19 from app2-app5 verify(mDisp, times(19)).handle( argThat(new IsPreemptionRequestFor(getAppAttemptId(2)))); verify(mDisp, times(19)).handle( argThat(new IsPreemptionRequestFor(getAppAttemptId(3)))); verify(mDisp, times(20)).handle( argThat(new IsPreemptionRequestFor(getAppAttemptId(4)))); verify(mDisp, times(20)).handle( argThat(new IsPreemptionRequestFor(getAppAttemptId(5)))); } @Test public void testNodePartitionDisablePreemptionForSingleLevelQueue() throws IOException { /** * Queue structure is: * * <pre> * root * / | \ * a b c * </pre> * * Both a/b/c can access x, and guaranteed_capacity(x) of them is 40:20:40. * a/b/c's max resource is 100. b is disable-preemption * * Two nodes, n1 has 100 x, n2 has 100 NO_LABEL. * * 2 apps in cluster. app1 in a (usage=50), app2 in b(usage=30), app3 in * c(usage=20). All of them have 50 pending resource. * * After preemption, app1 will be preempt 10 containers and app2 will not be * preempted */ String labelsConfig = "=100,true;" + // default partition "x=100,true"; // partition=x String nodesConfig = "n1=x;" + // n1 has partition=x "n2="; // n2 is default partition String queuesConfig = // guaranteed,max,used,pending "root(=[100 100 100 100],x=[100 100 100 100]);" + //root "-a(=[80 80 0 0],x=[40 100 50 50]);" + // a "-b(=[10 100 0 0],x=[20 100 30 0]);" + // b "-c(=[10 100 0 0],x=[40 100 20 50])"; //c String appsConfig= //queueName\t(priority,resource,host,expression,#repeat,reserved) "a\t" // app1 in a + "(1,1,n1,x,50,false);" + // 50x in n1 "b\t" // app2 in b + "(1,1,n1,x,30,false);" + // 30x in n1 "c\t" // app3 in c + "(1,1,n1,x,20,false)"; // 20x in n1 conf.setPreemptionDisabled("root.b", true); buildEnv(labelsConfig, nodesConfig, queuesConfig, appsConfig); policy.editSchedule(); // 10 preempted from app1, nothing preempted from app2-app3 verify(mDisp, times(10)).handle( argThat(new IsPreemptionRequestFor(getAppAttemptId(1)))); verify(mDisp, never()).handle( argThat(new IsPreemptionRequestFor(getAppAttemptId(2)))); verify(mDisp, never()).handle( argThat(new IsPreemptionRequestFor(getAppAttemptId(3)))); } @Test public void testNodePartitionNonAccessibleQueuesSharePartitionedResource() throws IOException { /** * Queue structure is: * * <pre> * root * _________ * / | | \ * a b c d * </pre> * * a/b can access x, their capacity is 50:50 c/d cannot access x. * * a uses 0, wants 30 * b(app1) uses 30, wants 0 * c(app2)&d(app3) use 35, wants 50 * * After preemption, c/d will be preempted 15 containers, because idle * resource = 100 - 30 (which is used by b) - 30 (which is asked by a) = 40 * will be divided by c/d, so each of c/d get 20. */ String labelsConfig = "=100,true;" + // default partition "x=100,false"; // partition=x String nodesConfig = "n1=x;" + // n1 has partition=x "n2="; // n2 is default partition String queuesConfig = // guaranteed,max,used,pending "root(=[100 100 100 100],x=[100 100 100 100]);" + //root "-a(=[25 100 0 0],x=[50 100 0 30]);" + // a "-b(=[25 100 0 0],x=[50 100 30 0]);" + // b "-c(=[25 100 1 0],x=[0 0 35 50]);" + //c "-d(=[25 100 1 0],x=[0 0 35 50])"; //d String appsConfig= //queueName\t(priority,resource,host,expression,#repeat,reserved) "b\t" // app1 in b + "(1,1,n1,x,30,false);" + // 50x in n1 "c\t" // app2 in c + "(1,1,n2,,1,false)" // AM container (in n2) + "(1,1,n1,,30,false);" + // 30x in n1 (ignore exclusivity) "d\t" // app3 in d + "(1,1,n2,,1,false)" // AM container (in n2) + "(1,1,n1,,30,false)"; // 30x in n1 (ignore exclusivity) buildEnv(labelsConfig, nodesConfig, queuesConfig, appsConfig); policy.editSchedule(); // 15 will be preempted app2/app3 verify(mDisp, times(15)).handle( argThat(new IsPreemptionRequestFor(getAppAttemptId(2)))); verify(mDisp, times(15)).handle( argThat(new IsPreemptionRequestFor(getAppAttemptId(3)))); verify(mDisp, never()).handle( argThat(new IsPreemptionRequestFor(getAppAttemptId(1)))); } @Test public void testHierarchyPreemptionForMultiplePartitions() throws IOException { /** * Queue structure is: * * <pre> * root * / \ * a b * / \ / \ * a1 a2 b1 b2 * </pre> * * Both a/b can access x/y, and in all hierarchy capacity ratio is 50:50. * So for a1/a2/b1/b2, all of them can access 25x, 25y * * a1 uses 35x, 25y * a2 uses 25x, 15y * b1 uses 15x, 25y * b2 uses 25x 35y * * So as a result, a2 will preempt from b2, and b1 will preempt from a1. * * After preemption, a1 will be preempted 10x and b2 will be preempted 10y. */ String labelsConfig = "=100,true;" + // default partition "x=100,true;" + // partition=x "y=100,true"; // partition=y String nodesConfig = "n1=x;" + // n1 has partition=x "n2=y;" + // n2 has partition=y "n3="; // n3 is default partition String queuesConfig = // guaranteed,max,used,pending "root(=[100 100 0 0],x=[100 100 100 100],y=[100 100 100 100]);" + //root "-a(=[50 100 0 0],x=[50 100 60 40],y=[50 100 40 40]);" + // a "--a1(=[25 100 0 0],x=[25 100 35 20],y=[25 100 25 20]);" + // a1 "--a2(=[25 100 0 0],x=[25 100 25 20],y=[25 100 15 20]);" + // a2 "-b(=[50 100 0 0],x=[50 100 40 40],y=[50 100 60 40]);" + // b "--b1(=[25 100 0 0],x=[25 100 15 20],y=[25 100 25 20]);" + // b1 "--b2(=[25 100 0 0],x=[25 100 25 20],y=[25 100 35 20])"; // b2 String appsConfig= //queueName\t(priority,resource,host,expression,#repeat,reserved) "a1\t" // app1 in a1 + "(1,1,n1,x,35,false)" // 35 of x + "(1,1,n2,y,25,false);" + // 25 of y "a2\t" // app2 in a2 + "(1,1,n1,x,25,false)" // 25 of x + "(1,1,n2,y,15,false);" + // 15 of y "b1\t" // app3 in b1 + "(1,1,n1,x,15,false)" // 15 of x + "(1,1,n2,y,25,false);" + // 25 of y "b2\t" // app4 in b2 + "(1,1,n1,x,25,false)" // 25 of x + "(1,1,n2,y,35,false)"; // 35 of y buildEnv(labelsConfig, nodesConfig, queuesConfig, appsConfig); policy.editSchedule(); // 10 will be preempted from app1 (a1) /app4 (b2) verify(mDisp, times(10)).handle( argThat(new IsPreemptionRequestFor(getAppAttemptId(1)))); verify(mDisp, times(10)).handle( argThat(new IsPreemptionRequestFor(getAppAttemptId(4)))); verify(mDisp, never()).handle( argThat(new IsPreemptionRequestFor(getAppAttemptId(2)))); verify(mDisp, never()).handle( argThat(new IsPreemptionRequestFor(getAppAttemptId(3)))); } @Test public void testHierarchyPreemptionForDifferenceAcessibility() throws IOException { /** * Queue structure is: * * <pre> * root * / \ * a b * / \ / \ * a1 a2 b1 b2 * </pre> * * a can access x only and b can access y only * * Capacities of a1/a2, b1/b2 is 50:50 * * a1 uses 100x and b1 uses 80y * * So as a result, a1 will be preempted 50 containers and b1 will be * preempted 30 containers */ String labelsConfig = "=100,true;" + // default partition "x=100,true;" + // partition=x "y=100,true"; // partition=y String nodesConfig = "n1=x;" + // n1 has partition=x "n2=y;" + // n2 has partition=y "n3="; // n3 is default partition String queuesConfig = // guaranteed,max,used,pending "root(=[100 100 0 0],x=[100 100 100 100],y=[100 100 100 100]);" + //root "-a(=[50 100 0 0],x=[100 100 100 100]);" + // a "--a1(=[25 100 0 0],x=[50 100 100 0]);" + // a1 "--a2(=[25 100 0 0],x=[50 100 0 100]);" + // a2 "-b(=[50 100 0 0],y=[100 100 80 100]);" + // b "--b1(=[25 100 0 0],y=[50 100 80 0]);" + // b1 "--b2(=[25 100 0 0],y=[50 100 0 100])"; // b2 String appsConfig= //queueName\t(priority,resource,host,expression,#repeat,reserved) "a1\t" // app1 in a1 + "(1,1,n1,x,100,false);" + // 100 of x "b1\t" // app2 in b1 + "(1,1,n2,y,80,false)"; // 80 of y buildEnv(labelsConfig, nodesConfig, queuesConfig, appsConfig); policy.editSchedule(); verify(mDisp, times(50)).handle( argThat(new IsPreemptionRequestFor(getAppAttemptId(1)))); verify(mDisp, times(30)).handle( argThat(new IsPreemptionRequestFor(getAppAttemptId(2)))); } @Test public void testNodePartitionPreemptionWithVCoreResource() throws IOException { /** * Queue structure is: * * <pre> * root * / \ * a b * </pre> * * Both a/b can access x, and guaranteed capacity of them is 50:50. Two * nodes, n1 has 100 x, n2 has 100 NO_LABEL 4 applications in the cluster, * app1/app2 in a, and app3/app4 in b. app1 uses 80 x, app2 uses 20 * NO_LABEL, app3 uses 20 x, app4 uses 80 NO_LABEL. Both a/b have 50 pending * resource for x and NO_LABEL * * After preemption, it should preempt 30 from app1, and 30 from app4. */ String labelsConfig = "=100:200,true;" + // default partition "x=100:200,true"; // partition=x String nodesConfig = "n1=x;" + // n1 has partition=x "n2="; // n2 is default partition String queuesConfig = // guaranteed,max,used,pending "root(=[100:200 100:200 100:200 100:200],x=[100:200 100:200 100:200 100:200]);" + // root "-a(=[50:100 100:200 20:40 50:100],x=[50:100 100:200 80:160 50:100]);" + // a "-b(=[50:100 100:200 80:160 50:100],x=[50:100 100:200 20:40 50:100])"; // b String appsConfig = // queueName\t(priority,resource,host,expression,#repeat,reserved) "a\t" // app1 in a + "(1,1:2,n1,x,80,false);" + // 80 * x in n1 "a\t" // app2 in a + "(1,1:2,n2,,20,false);" + // 20 default in n2 "b\t" // app3 in b + "(1,1:2,n1,x,20,false);" + // 20 * x in n1 "b\t" // app4 in b + "(1,1:2,n2,,80,false)"; // 80 default in n2 buildEnv(labelsConfig, nodesConfig, queuesConfig, appsConfig, true); policy.editSchedule(); // 30 preempted from app1, 30 preempted from app4, and nothing preempted // from app2/app3 verify(mDisp, times(30)).handle( argThat(new IsPreemptionRequestFor(getAppAttemptId(1)))); verify(mDisp, times(30)).handle( argThat(new IsPreemptionRequestFor(getAppAttemptId(4)))); verify(mDisp, never()).handle( argThat(new IsPreemptionRequestFor(getAppAttemptId(2)))); verify(mDisp, never()).handle( argThat(new IsPreemptionRequestFor(getAppAttemptId(3)))); } }