|
@@ -0,0 +1,541 @@
|
|
|
|
+/**
|
|
|
|
+ * 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 java.util.ArrayList;
|
|
|
|
+import java.util.Comparator;
|
|
|
|
+import java.util.Deque;
|
|
|
|
+import java.util.LinkedList;
|
|
|
|
+import java.util.List;
|
|
|
|
+import java.util.NavigableSet;
|
|
|
|
+import java.util.Random;
|
|
|
|
+import java.util.TreeSet;
|
|
|
|
+
|
|
|
|
+import org.apache.hadoop.conf.Configuration;
|
|
|
|
+import org.apache.hadoop.yarn.api.records.ApplicationAttemptId;
|
|
|
|
+import org.apache.hadoop.yarn.api.records.ApplicationId;
|
|
|
|
+import org.apache.hadoop.yarn.api.records.Container;
|
|
|
|
+import org.apache.hadoop.yarn.api.records.ContainerId;
|
|
|
|
+import org.apache.hadoop.yarn.api.records.Resource;
|
|
|
|
+import org.apache.hadoop.yarn.event.EventHandler;
|
|
|
|
+import org.apache.hadoop.yarn.server.resourcemanager.resource.Priority;
|
|
|
|
+import org.apache.hadoop.yarn.server.resourcemanager.rmcontainer.RMContainer;
|
|
|
|
+import org.apache.hadoop.yarn.server.resourcemanager.scheduler.ContainerPreemptEvent;
|
|
|
|
+import org.apache.hadoop.yarn.server.resourcemanager.scheduler.ContainerPreemptEventType;
|
|
|
|
+import org.apache.hadoop.yarn.server.resourcemanager.scheduler.capacity.CSQueue;
|
|
|
|
+import org.apache.hadoop.yarn.server.resourcemanager.scheduler.capacity.CapacityScheduler;
|
|
|
|
+import org.apache.hadoop.yarn.server.resourcemanager.scheduler.capacity.LeafQueue;
|
|
|
|
+import org.apache.hadoop.yarn.server.resourcemanager.scheduler.capacity.ParentQueue;
|
|
|
|
+import org.apache.hadoop.yarn.server.resourcemanager.scheduler.common.fica.FiCaSchedulerApp;
|
|
|
|
+import org.apache.hadoop.yarn.util.Clock;
|
|
|
|
+import org.apache.hadoop.yarn.util.resource.DefaultResourceCalculator;
|
|
|
|
+import org.apache.hadoop.yarn.util.resource.ResourceCalculator;
|
|
|
|
+import org.junit.Before;
|
|
|
|
+import org.junit.Rule;
|
|
|
|
+import org.junit.Test;
|
|
|
|
+import org.junit.rules.TestName;
|
|
|
|
+import org.mockito.ArgumentCaptor;
|
|
|
|
+import org.mockito.ArgumentMatcher;
|
|
|
|
+
|
|
|
|
+import static org.apache.hadoop.yarn.server.resourcemanager.monitor.capacity.ProportionalCapacityPreemptionPolicy.MAX_IGNORED_OVER_CAPACITY;
|
|
|
|
+import static org.apache.hadoop.yarn.server.resourcemanager.monitor.capacity.ProportionalCapacityPreemptionPolicy.MONITORING_INTERVAL;
|
|
|
|
+import static org.apache.hadoop.yarn.server.resourcemanager.monitor.capacity.ProportionalCapacityPreemptionPolicy.NATURAL_TERMINATION_FACTOR;
|
|
|
|
+import static org.apache.hadoop.yarn.server.resourcemanager.monitor.capacity.ProportionalCapacityPreemptionPolicy.OBSERVE_ONLY;
|
|
|
|
+import static org.apache.hadoop.yarn.server.resourcemanager.monitor.capacity.ProportionalCapacityPreemptionPolicy.TOTAL_PREEMPTION_PER_ROUND;
|
|
|
|
+import static org.apache.hadoop.yarn.server.resourcemanager.monitor.capacity.ProportionalCapacityPreemptionPolicy.WAIT_TIME_BEFORE_KILL;
|
|
|
|
+import static org.apache.hadoop.yarn.server.resourcemanager.scheduler.ContainerPreemptEventType.KILL_CONTAINER;
|
|
|
|
+import static org.apache.hadoop.yarn.server.resourcemanager.scheduler.ContainerPreemptEventType.PREEMPT_CONTAINER;
|
|
|
|
+import static org.junit.Assert.*;
|
|
|
|
+import static org.mockito.Mockito.*;
|
|
|
|
+
|
|
|
|
+public class TestProportionalCapacityPreemptionPolicy {
|
|
|
|
+
|
|
|
|
+ static final long TS = 3141592653L;
|
|
|
|
+
|
|
|
|
+ int appAlloc = 0;
|
|
|
|
+ Random rand = null;
|
|
|
|
+ Clock mClock = null;
|
|
|
|
+ Configuration conf = null;
|
|
|
|
+ CapacityScheduler mCS = null;
|
|
|
|
+ EventHandler<ContainerPreemptEvent> mDisp = null;
|
|
|
|
+ ResourceCalculator rc = new DefaultResourceCalculator();
|
|
|
|
+ final ApplicationAttemptId appA = ApplicationAttemptId.newInstance(
|
|
|
|
+ ApplicationId.newInstance(TS, 0), 0);
|
|
|
|
+ final ApplicationAttemptId appB = ApplicationAttemptId.newInstance(
|
|
|
|
+ ApplicationId.newInstance(TS, 1), 0);
|
|
|
|
+ final ApplicationAttemptId appC = ApplicationAttemptId.newInstance(
|
|
|
|
+ ApplicationId.newInstance(TS, 2), 0);
|
|
|
|
+ final ApplicationAttemptId appD = ApplicationAttemptId.newInstance(
|
|
|
|
+ ApplicationId.newInstance(TS, 3), 0);
|
|
|
|
+ final ApplicationAttemptId appE = ApplicationAttemptId.newInstance(
|
|
|
|
+ ApplicationId.newInstance(TS, 4), 0);
|
|
|
|
+ final ArgumentCaptor<ContainerPreemptEvent> evtCaptor =
|
|
|
|
+ ArgumentCaptor.forClass(ContainerPreemptEvent.class);
|
|
|
|
+
|
|
|
|
+ @Rule public TestName name = new TestName();
|
|
|
|
+
|
|
|
|
+ @Before
|
|
|
|
+ @SuppressWarnings("unchecked")
|
|
|
|
+ public void setup() {
|
|
|
|
+ conf = new Configuration(false);
|
|
|
|
+ conf.setLong(WAIT_TIME_BEFORE_KILL, 10000);
|
|
|
|
+ conf.setLong(MONITORING_INTERVAL, 3000);
|
|
|
|
+ // report "ideal" preempt
|
|
|
|
+ conf.setFloat(TOTAL_PREEMPTION_PER_ROUND, (float) 1.0);
|
|
|
|
+ conf.setFloat(NATURAL_TERMINATION_FACTOR, (float) 1.0);
|
|
|
|
+
|
|
|
|
+ mClock = mock(Clock.class);
|
|
|
|
+ mCS = mock(CapacityScheduler.class);
|
|
|
|
+ when(mCS.getResourceCalculator()).thenReturn(rc);
|
|
|
|
+ mDisp = mock(EventHandler.class);
|
|
|
|
+ rand = new Random();
|
|
|
|
+ long seed = rand.nextLong();
|
|
|
|
+ System.out.println(name.getMethodName() + " SEED: " + seed);
|
|
|
|
+ rand.setSeed(seed);
|
|
|
|
+ appAlloc = 0;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Test
|
|
|
|
+ public void testIgnore() {
|
|
|
|
+ int[][] qData = new int[][]{
|
|
|
|
+ // / A B C
|
|
|
|
+ { 100, 40, 40, 20 }, // abs
|
|
|
|
+ { 100, 0, 60, 40 }, // used
|
|
|
|
+ { 0, 0, 0, 0 }, // pending
|
|
|
|
+ { 0, 0, 0, 0 }, // reserved
|
|
|
|
+ { 3, 1, 1, 1 }, // apps
|
|
|
|
+ { -1, 1, 1, 1 }, // req granularity
|
|
|
|
+ { 3, 0, 0, 0 }, // subqueues
|
|
|
|
+ };
|
|
|
|
+ ProportionalCapacityPreemptionPolicy policy = buildPolicy(qData);
|
|
|
|
+ policy.editSchedule();
|
|
|
|
+ // don't correct imbalances without demand
|
|
|
|
+ verify(mDisp, never()).handle(isA(ContainerPreemptEvent.class));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Test
|
|
|
|
+ public void testProportionalPreemption() {
|
|
|
|
+ int[][] qData = new int[][]{
|
|
|
|
+ // / A B C D
|
|
|
|
+ { 100, 10, 40, 20, 30 }, // abs
|
|
|
|
+ { 100, 30, 60, 10, 0 }, // used
|
|
|
|
+ { 45, 20, 5, 20, 0 }, // pending
|
|
|
|
+ { 0, 0, 0, 0, 0 }, // reserved
|
|
|
|
+ { 3, 1, 1, 1, 0 }, // apps
|
|
|
|
+ { -1, 1, 1, 1, 1 }, // req granularity
|
|
|
|
+ { 4, 0, 0, 0, 0 }, // subqueues
|
|
|
|
+ };
|
|
|
|
+ ProportionalCapacityPreemptionPolicy policy = buildPolicy(qData);
|
|
|
|
+ policy.editSchedule();
|
|
|
|
+ verify(mDisp, times(16)).handle(argThat(new IsPreemptionRequestFor(appA)));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Test
|
|
|
|
+ public void testPreemptCycle() {
|
|
|
|
+ int[][] qData = new int[][]{
|
|
|
|
+ // / A B C
|
|
|
|
+ { 100, 40, 40, 20 }, // abs
|
|
|
|
+ { 100, 0, 60, 40 }, // used
|
|
|
|
+ { 10, 10, 0, 0 }, // pending
|
|
|
|
+ { 0, 0, 0, 0 }, // reserved
|
|
|
|
+ { 3, 1, 1, 1 }, // apps
|
|
|
|
+ { -1, 1, 1, 1 }, // req granularity
|
|
|
|
+ { 3, 0, 0, 0 }, // subqueues
|
|
|
|
+ };
|
|
|
|
+ ProportionalCapacityPreemptionPolicy policy = buildPolicy(qData);
|
|
|
|
+ policy.editSchedule();
|
|
|
|
+ // ensure all pending rsrc from A get preempted from other queues
|
|
|
|
+ verify(mDisp, times(10)).handle(argThat(new IsPreemptionRequestFor(appC)));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Test
|
|
|
|
+ public void testExpireKill() {
|
|
|
|
+ final long killTime = 10000L;
|
|
|
|
+ int[][] qData = new int[][]{
|
|
|
|
+ // / A B C
|
|
|
|
+ { 100, 40, 40, 20 }, // abs
|
|
|
|
+ { 100, 0, 60, 40 }, // used
|
|
|
|
+ { 10, 10, 0, 0 }, // pending
|
|
|
|
+ { 0, 0, 0, 0 }, // reserved
|
|
|
|
+ { 3, 1, 1, 1 }, // apps
|
|
|
|
+ { -1, 1, 1, 1 }, // req granularity
|
|
|
|
+ { 3, 0, 0, 0 }, // subqueues
|
|
|
|
+ };
|
|
|
|
+ conf.setLong(WAIT_TIME_BEFORE_KILL, killTime);
|
|
|
|
+ ProportionalCapacityPreemptionPolicy policy = buildPolicy(qData);
|
|
|
|
+
|
|
|
|
+ // ensure all pending rsrc from A get preempted from other queues
|
|
|
|
+ when(mClock.getTime()).thenReturn(0L);
|
|
|
|
+ policy.editSchedule();
|
|
|
|
+ verify(mDisp, times(10)).handle(argThat(new IsPreemptionRequestFor(appC)));
|
|
|
|
+
|
|
|
|
+ // requests reiterated
|
|
|
|
+ when(mClock.getTime()).thenReturn(killTime / 2);
|
|
|
|
+ policy.editSchedule();
|
|
|
|
+ verify(mDisp, times(20)).handle(argThat(new IsPreemptionRequestFor(appC)));
|
|
|
|
+
|
|
|
|
+ // kill req sent
|
|
|
|
+ when(mClock.getTime()).thenReturn(killTime + 1);
|
|
|
|
+ policy.editSchedule();
|
|
|
|
+ verify(mDisp, times(30)).handle(evtCaptor.capture());
|
|
|
|
+ List<ContainerPreemptEvent> events = evtCaptor.getAllValues();
|
|
|
|
+ for (ContainerPreemptEvent e : events.subList(20, 30)) {
|
|
|
|
+ assertEquals(appC, e.getAppId());
|
|
|
|
+ assertEquals(KILL_CONTAINER, e.getType());
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Test
|
|
|
|
+ public void testDeadzone() {
|
|
|
|
+ int[][] qData = new int[][]{
|
|
|
|
+ // / A B C
|
|
|
|
+ { 100, 40, 40, 20 }, // abs
|
|
|
|
+ { 100, 39, 43, 21 }, // used
|
|
|
|
+ { 10, 10, 0, 0 }, // pending
|
|
|
|
+ { 0, 0, 0, 0 }, // reserved
|
|
|
|
+ { 3, 1, 1, 1 }, // apps
|
|
|
|
+ { -1, 1, 1, 1 }, // req granularity
|
|
|
|
+ { 3, 0, 0, 0 }, // subqueues
|
|
|
|
+ };
|
|
|
|
+ conf.setFloat(MAX_IGNORED_OVER_CAPACITY, (float) 0.1);
|
|
|
|
+ ProportionalCapacityPreemptionPolicy policy = buildPolicy(qData);
|
|
|
|
+ policy.editSchedule();
|
|
|
|
+ // ignore 10% overcapacity to avoid jitter
|
|
|
|
+ verify(mDisp, never()).handle(isA(ContainerPreemptEvent.class));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Test
|
|
|
|
+ public void testOverCapacityImbalance() {
|
|
|
|
+ int[][] qData = new int[][]{
|
|
|
|
+ // / A B C
|
|
|
|
+ { 100, 40, 40, 20 }, // abs
|
|
|
|
+ { 100, 55, 45, 0 }, // used
|
|
|
|
+ { 20, 10, 10, 0 }, // pending
|
|
|
|
+ { 0, 0, 0, 0 }, // reserved
|
|
|
|
+ { 2, 1, 1, 0 }, // apps
|
|
|
|
+ { -1, 1, 1, 0 }, // req granularity
|
|
|
|
+ { 3, 0, 0, 0 }, // subqueues
|
|
|
|
+ };
|
|
|
|
+ ProportionalCapacityPreemptionPolicy policy = buildPolicy(qData);
|
|
|
|
+ policy.editSchedule();
|
|
|
|
+ // correct imbalance between over-capacity queues
|
|
|
|
+ verify(mDisp, times(5)).handle(argThat(new IsPreemptionRequestFor(appA)));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Test
|
|
|
|
+ public void testNaturalTermination() {
|
|
|
|
+ int[][] qData = new int[][]{
|
|
|
|
+ // / A B C
|
|
|
|
+ { 100, 40, 40, 20 }, // abs
|
|
|
|
+ { 100, 55, 45, 0 }, // used
|
|
|
|
+ { 20, 10, 10, 0 }, // pending
|
|
|
|
+ { 0, 0, 0, 0 }, // reserved
|
|
|
|
+ { 2, 1, 1, 0 }, // apps
|
|
|
|
+ { -1, 1, 1, 0 }, // req granularity
|
|
|
|
+ { 3, 0, 0, 0 }, // subqueues
|
|
|
|
+ };
|
|
|
|
+ conf.setFloat(NATURAL_TERMINATION_FACTOR, (float) 0.1);
|
|
|
|
+ ProportionalCapacityPreemptionPolicy policy = buildPolicy(qData);
|
|
|
|
+ policy.editSchedule();
|
|
|
|
+ // ignore 10% imbalance between over-capacity queues
|
|
|
|
+ verify(mDisp, never()).handle(isA(ContainerPreemptEvent.class));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Test
|
|
|
|
+ public void testObserveOnly() {
|
|
|
|
+ int[][] qData = new int[][]{
|
|
|
|
+ // / A B C
|
|
|
|
+ { 100, 40, 40, 20 }, // abs
|
|
|
|
+ { 100, 90, 10, 0 }, // used
|
|
|
|
+ { 80, 10, 20, 50 }, // pending
|
|
|
|
+ { 0, 0, 0, 0 }, // reserved
|
|
|
|
+ { 2, 1, 1, 0 }, // apps
|
|
|
|
+ { -1, 1, 1, 0 }, // req granularity
|
|
|
|
+ { 3, 0, 0, 0 }, // subqueues
|
|
|
|
+ };
|
|
|
|
+ conf.setBoolean(OBSERVE_ONLY, true);
|
|
|
|
+ ProportionalCapacityPreemptionPolicy policy = buildPolicy(qData);
|
|
|
|
+ policy.editSchedule();
|
|
|
|
+ // verify even severe imbalance not affected
|
|
|
|
+ verify(mDisp, never()).handle(isA(ContainerPreemptEvent.class));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Test
|
|
|
|
+ public void testHierarchical() {
|
|
|
|
+ int[][] qData = new int[][] {
|
|
|
|
+ // / A B C D E F
|
|
|
|
+ { 200, 100, 50, 50, 100, 10, 90 }, // abs
|
|
|
|
+ { 200, 110, 60, 50, 90, 90, 0 }, // used
|
|
|
|
+ { 10, 0, 0, 0, 10, 0, 10 }, // pending
|
|
|
|
+ { 0, 0, 0, 0, 0, 0, 0 }, // reserved
|
|
|
|
+ { 4, 2, 1, 1, 2, 1, 1 }, // apps
|
|
|
|
+ { -1, -1, 1, 1, -1, 1, 1 }, // req granularity
|
|
|
|
+ { 2, 2, 0, 0, 2, 0, 0 }, // subqueues
|
|
|
|
+ };
|
|
|
|
+ ProportionalCapacityPreemptionPolicy policy = buildPolicy(qData);
|
|
|
|
+ policy.editSchedule();
|
|
|
|
+ // verify capacity taken from A1, not B1 despite B1 being far over
|
|
|
|
+ // its absolute guaranteed capacity
|
|
|
|
+ verify(mDisp, times(10)).handle(argThat(new IsPreemptionRequestFor(appA)));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Test
|
|
|
|
+ public void testHierarchicalLarge() {
|
|
|
|
+ int[][] qData = new int[][] {
|
|
|
|
+ // / A B C D E F G H I
|
|
|
|
+ { 400, 200, 60,140, 100, 70, 30, 100, 10, 90 }, // abs
|
|
|
|
+ { 400, 210, 70,140, 100, 50, 50, 90, 90, 0 }, // used
|
|
|
|
+ { 10, 0, 0, 0, 0, 0, 0, 0, 0, 15 }, // pending
|
|
|
|
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // reserved
|
|
|
|
+ { 6, 2, 1, 1, 2, 1, 1, 2, 1, 1 }, // apps
|
|
|
|
+ { -1, -1, 1, 1, -1, 1, 1, -1, 1, 1 }, // req granularity
|
|
|
|
+ { 3, 2, 0, 0, 2, 0, 0, 2, 0, 0 }, // subqueues
|
|
|
|
+ };
|
|
|
|
+ ProportionalCapacityPreemptionPolicy policy = buildPolicy(qData);
|
|
|
|
+ policy.editSchedule();
|
|
|
|
+ // verify capacity taken from A1, not H1 despite H1 being far over
|
|
|
|
+ // its absolute guaranteed capacity
|
|
|
|
+
|
|
|
|
+ // XXX note: compensating for rounding error in Resources.multiplyTo
|
|
|
|
+ // which is likely triggered since we use small numbers for readability
|
|
|
|
+ verify(mDisp, times(9)).handle(argThat(new IsPreemptionRequestFor(appA)));
|
|
|
|
+ verify(mDisp, times(4)).handle(argThat(new IsPreemptionRequestFor(appE)));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Test
|
|
|
|
+ public void testContainerOrdering(){
|
|
|
|
+
|
|
|
|
+ List<RMContainer> containers = new ArrayList<RMContainer>();
|
|
|
|
+
|
|
|
|
+ ApplicationAttemptId appAttId = ApplicationAttemptId.newInstance(
|
|
|
|
+ ApplicationId.newInstance(TS, 10), 0);
|
|
|
|
+
|
|
|
|
+ // create a set of containers
|
|
|
|
+ RMContainer rm1 = mockContainer(appAttId, 5, mock(Resource.class), 3);
|
|
|
|
+ RMContainer rm2 = mockContainer(appAttId, 3, mock(Resource.class), 3);
|
|
|
|
+ RMContainer rm3 = mockContainer(appAttId, 2, mock(Resource.class), 2);
|
|
|
|
+ RMContainer rm4 = mockContainer(appAttId, 1, mock(Resource.class), 2);
|
|
|
|
+ RMContainer rm5 = mockContainer(appAttId, 4, mock(Resource.class), 1);
|
|
|
|
+
|
|
|
|
+ // insert them in non-sorted order
|
|
|
|
+ containers.add(rm3);
|
|
|
|
+ containers.add(rm2);
|
|
|
|
+ containers.add(rm1);
|
|
|
|
+ containers.add(rm5);
|
|
|
|
+ containers.add(rm4);
|
|
|
|
+
|
|
|
|
+ // sort them
|
|
|
|
+ ProportionalCapacityPreemptionPolicy.sortContainers(containers);
|
|
|
|
+
|
|
|
|
+ // verify the "priority"-first, "reverse container-id"-second
|
|
|
|
+ // ordering is enforced correctly
|
|
|
|
+ assert containers.get(0).equals(rm1);
|
|
|
|
+ assert containers.get(1).equals(rm2);
|
|
|
|
+ assert containers.get(2).equals(rm3);
|
|
|
|
+ assert containers.get(3).equals(rm4);
|
|
|
|
+ assert containers.get(4).equals(rm5);
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ static class IsPreemptionRequestFor
|
|
|
|
+ extends ArgumentMatcher<ContainerPreemptEvent> {
|
|
|
|
+ private final ApplicationAttemptId appAttId;
|
|
|
|
+ private final ContainerPreemptEventType type;
|
|
|
|
+ IsPreemptionRequestFor(ApplicationAttemptId appAttId) {
|
|
|
|
+ this(appAttId, PREEMPT_CONTAINER);
|
|
|
|
+ }
|
|
|
|
+ IsPreemptionRequestFor(ApplicationAttemptId appAttId,
|
|
|
|
+ ContainerPreemptEventType type) {
|
|
|
|
+ this.appAttId = appAttId;
|
|
|
|
+ this.type = type;
|
|
|
|
+ }
|
|
|
|
+ @Override
|
|
|
|
+ public boolean matches(Object o) {
|
|
|
|
+ return appAttId.equals(((ContainerPreemptEvent)o).getAppId())
|
|
|
|
+ && type.equals(((ContainerPreemptEvent)o).getType());
|
|
|
|
+ }
|
|
|
|
+ @Override
|
|
|
|
+ public String toString() {
|
|
|
|
+ return appAttId.toString();
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ ProportionalCapacityPreemptionPolicy buildPolicy(int[][] qData) {
|
|
|
|
+ ProportionalCapacityPreemptionPolicy policy =
|
|
|
|
+ new ProportionalCapacityPreemptionPolicy(conf, mDisp, mCS, mClock);
|
|
|
|
+ ParentQueue mRoot = buildMockRootQueue(rand, qData);
|
|
|
|
+ when(mCS.getRootQueue()).thenReturn(mRoot);
|
|
|
|
+
|
|
|
|
+ Resource clusterResources =
|
|
|
|
+ Resource.newInstance(leafAbsCapacities(qData[0], qData[6]), 0);
|
|
|
|
+ when(mCS.getClusterResources()).thenReturn(clusterResources);
|
|
|
|
+ return policy;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ ParentQueue buildMockRootQueue(Random r, int[]... queueData) {
|
|
|
|
+ int[] abs = queueData[0];
|
|
|
|
+ int[] used = queueData[1];
|
|
|
|
+ int[] pending = queueData[2];
|
|
|
|
+ int[] reserved = queueData[3];
|
|
|
|
+ int[] apps = queueData[4];
|
|
|
|
+ int[] gran = queueData[5];
|
|
|
|
+ int[] queues = queueData[6];
|
|
|
|
+
|
|
|
|
+ return mockNested(abs, used, pending, reserved, apps, gran, queues);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ ParentQueue mockNested(int[] abs, int[] used,
|
|
|
|
+ int[] pending, int[] reserved, int[] apps, int[] gran, int[] queues) {
|
|
|
|
+ float tot = leafAbsCapacities(abs, queues);
|
|
|
|
+ Deque<ParentQueue> pqs = new LinkedList<ParentQueue>();
|
|
|
|
+ ParentQueue root = mockParentQueue(null, queues[0], pqs);
|
|
|
|
+ when(root.getQueueName()).thenReturn("/");
|
|
|
|
+ when(root.getAbsoluteUsedCapacity()).thenReturn(used[0] / tot);
|
|
|
|
+ when(root.getAbsoluteCapacity()).thenReturn(abs[0] / tot);
|
|
|
|
+ for (int i = 1; i < queues.length; ++i) {
|
|
|
|
+ final CSQueue q;
|
|
|
|
+ final ParentQueue p = pqs.removeLast();
|
|
|
|
+ final String queueName = "queue" + ((char)('A' + i - 1));
|
|
|
|
+ if (queues[i] > 0) {
|
|
|
|
+ q = mockParentQueue(p, queues[i], pqs);
|
|
|
|
+ } else {
|
|
|
|
+ q = mockLeafQueue(p, tot, i, abs, used, pending, reserved, apps, gran);
|
|
|
|
+ }
|
|
|
|
+ when(q.getParent()).thenReturn(p);
|
|
|
|
+ when(q.getQueueName()).thenReturn(queueName);
|
|
|
|
+ when(q.getAbsoluteUsedCapacity()).thenReturn(used[i] / tot);
|
|
|
|
+ when(q.getAbsoluteCapacity()).thenReturn(abs[i] / tot);
|
|
|
|
+ }
|
|
|
|
+ assert 0 == pqs.size();
|
|
|
|
+ return root;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ ParentQueue mockParentQueue(ParentQueue p, int subqueues,
|
|
|
|
+ Deque<ParentQueue> pqs) {
|
|
|
|
+ ParentQueue pq = mock(ParentQueue.class);
|
|
|
|
+ List<CSQueue> cqs = new ArrayList<CSQueue>();
|
|
|
|
+ when(pq.getChildQueues()).thenReturn(cqs);
|
|
|
|
+ for (int i = 0; i < subqueues; ++i) {
|
|
|
|
+ pqs.add(pq);
|
|
|
|
+ }
|
|
|
|
+ if (p != null) {
|
|
|
|
+ p.getChildQueues().add(pq);
|
|
|
|
+ }
|
|
|
|
+ return pq;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ LeafQueue mockLeafQueue(ParentQueue p, float tot, int i, int[] abs,
|
|
|
|
+ int[] used, int[] pending, int[] reserved, int[] apps, int[] gran) {
|
|
|
|
+ LeafQueue lq = mock(LeafQueue.class);
|
|
|
|
+ when(lq.getTotalResourcePending()).thenReturn(
|
|
|
|
+ Resource.newInstance(pending[i], 0));
|
|
|
|
+ // consider moving where CapacityScheduler::comparator accessible
|
|
|
|
+ NavigableSet<FiCaSchedulerApp> qApps = new TreeSet<FiCaSchedulerApp>(
|
|
|
|
+ new Comparator<FiCaSchedulerApp>() {
|
|
|
|
+ @Override
|
|
|
|
+ public int compare(FiCaSchedulerApp a1, FiCaSchedulerApp a2) {
|
|
|
|
+ return a1.getApplicationAttemptId()
|
|
|
|
+ .compareTo(a2.getApplicationAttemptId());
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+ // applications are added in global L->R order in queues
|
|
|
|
+ if (apps[i] != 0) {
|
|
|
|
+ int aUsed = used[i] / apps[i];
|
|
|
|
+ int aPending = pending[i] / apps[i];
|
|
|
|
+ int aReserve = reserved[i] / apps[i];
|
|
|
|
+ for (int a = 0; a < apps[i]; ++a) {
|
|
|
|
+ qApps.add(mockApp(i, appAlloc, aUsed, aPending, aReserve, gran[i]));
|
|
|
|
+ ++appAlloc;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ when(lq.getApplications()).thenReturn(qApps);
|
|
|
|
+ p.getChildQueues().add(lq);
|
|
|
|
+ return lq;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ FiCaSchedulerApp mockApp(int qid, int id, int used, int pending, int reserved,
|
|
|
|
+ int gran) {
|
|
|
|
+ FiCaSchedulerApp app = mock(FiCaSchedulerApp.class);
|
|
|
|
+
|
|
|
|
+ ApplicationId appId = ApplicationId.newInstance(TS, id);
|
|
|
|
+ ApplicationAttemptId appAttId = ApplicationAttemptId.newInstance(appId, 0);
|
|
|
|
+ when(app.getApplicationId()).thenReturn(appId);
|
|
|
|
+ when(app.getApplicationAttemptId()).thenReturn(appAttId);
|
|
|
|
+
|
|
|
|
+ int cAlloc = 0;
|
|
|
|
+ Resource unit = Resource.newInstance(gran, 0);
|
|
|
|
+ List<RMContainer> cReserved = new ArrayList<RMContainer>();
|
|
|
|
+ for (int i = 0; i < reserved; i += gran) {
|
|
|
|
+ cReserved.add(mockContainer(appAttId, cAlloc, unit, 1));
|
|
|
|
+ ++cAlloc;
|
|
|
|
+ }
|
|
|
|
+ when(app.getReservedContainers()).thenReturn(cReserved);
|
|
|
|
+
|
|
|
|
+ List<RMContainer> cLive = new ArrayList<RMContainer>();
|
|
|
|
+ for (int i = 0; i < used; i += gran) {
|
|
|
|
+ cLive.add(mockContainer(appAttId, cAlloc, unit, 1));
|
|
|
|
+ ++cAlloc;
|
|
|
|
+ }
|
|
|
|
+ when(app.getLiveContainers()).thenReturn(cLive);
|
|
|
|
+ return app;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ RMContainer mockContainer(ApplicationAttemptId appAttId, int id,
|
|
|
|
+ Resource r, int priority) {
|
|
|
|
+ ContainerId cId = ContainerId.newInstance(appAttId, id);
|
|
|
|
+ Container c = mock(Container.class);
|
|
|
|
+ when(c.getResource()).thenReturn(r);
|
|
|
|
+ when(c.getPriority()).thenReturn(Priority.create(priority));
|
|
|
|
+ RMContainer mC = mock(RMContainer.class);
|
|
|
|
+ when(mC.getContainerId()).thenReturn(cId);
|
|
|
|
+ when(mC.getContainer()).thenReturn(c);
|
|
|
|
+ return mC;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ static int leafAbsCapacities(int[] abs, int[] subqueues) {
|
|
|
|
+ int ret = 0;
|
|
|
|
+ for (int i = 0; i < abs.length; ++i) {
|
|
|
|
+ if (0 == subqueues[i]) {
|
|
|
|
+ ret += abs[i];
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ return ret;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ void printString(CSQueue nq, String indent) {
|
|
|
|
+ if (nq instanceof ParentQueue) {
|
|
|
|
+ System.out.println(indent + nq.getQueueName()
|
|
|
|
+ + " cur:" + nq.getAbsoluteUsedCapacity()
|
|
|
|
+ + " guar:" + nq.getAbsoluteCapacity()
|
|
|
|
+ );
|
|
|
|
+ for (CSQueue q : ((ParentQueue)nq).getChildQueues()) {
|
|
|
|
+ printString(q, indent + " ");
|
|
|
|
+ }
|
|
|
|
+ } else {
|
|
|
|
+ System.out.println(indent + nq.getQueueName()
|
|
|
|
+ + " pen:" + ((LeafQueue) nq).getTotalResourcePending()
|
|
|
|
+ + " cur:" + nq.getAbsoluteUsedCapacity()
|
|
|
|
+ + " guar:" + nq.getAbsoluteCapacity()
|
|
|
|
+ );
|
|
|
|
+ for (FiCaSchedulerApp a : ((LeafQueue)nq).getApplications()) {
|
|
|
|
+ System.out.println(indent + " " + a.getApplicationId());
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+}
|