blob: 1fd455a607a71b2d1a373c51c098bcc3f907fc1f [file] [log] [blame]
/**
* 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))));
}
}