Skip to content

Commit 7c7adef

Browse files
YARN-11801: NPE in FifoCandidatesSelector.selectCandidates when preempting resources for an auto-created queue without child queues (#7607)
1 parent 72939fe commit 7c7adef

File tree

3 files changed

+82
-38
lines changed

3 files changed

+82
-38
lines changed

hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/monitor/capacity/ProportionalCapacityPreemptionPolicy.java

+9-8
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import org.apache.hadoop.thirdparty.com.google.common.collect.ImmutableSet;
2323
import org.apache.commons.lang3.StringUtils;
2424
import org.apache.hadoop.yarn.server.resourcemanager.scheduler.capacity.AbstractParentQueue;
25+
import org.apache.hadoop.yarn.server.resourcemanager.scheduler.capacity.AbstractLeafQueue;
2526
import org.slf4j.Logger;
2627
import org.slf4j.LoggerFactory;
2728
import org.apache.hadoop.classification.InterfaceStability.Unstable;
@@ -40,8 +41,6 @@
4041
import org.apache.hadoop.yarn.server.resourcemanager.scheduler.capacity.CapacityScheduler;
4142
import org.apache.hadoop.yarn.server.resourcemanager.scheduler.capacity.CapacitySchedulerConfiguration;
4243

43-
import org.apache.hadoop.yarn.server.resourcemanager.scheduler.capacity
44-
.ManagedParentQueue;
4544
import org.apache.hadoop.yarn.server.resourcemanager.scheduler.capacity.QueueCapacities;
4645
import org.apache.hadoop.yarn.server.resourcemanager.scheduler.capacity.preemption.PreemptableQueue;
4746
import org.apache.hadoop.yarn.server.resourcemanager.scheduler.event.ContainerPreemptEvent;
@@ -430,12 +429,14 @@ private void cleanupStaledPreemptionCandidates(long currentTime) {
430429
}
431430

432431
private Set<String> getLeafQueueNames(TempQueuePerPartition q) {
433-
// Also exclude ParentQueues, which might be without children
434-
if (CollectionUtils.isEmpty(q.children)
435-
&& !(q.parentQueue instanceof ManagedParentQueue)
436-
&& (q.parentQueue == null
437-
|| !q.parentQueue.isEligibleForAutoQueueCreation())) {
438-
return ImmutableSet.of(q.queueName);
432+
// Only consider this a leaf queue if:
433+
// It is a concrete leaf queue (not a childless parent)
434+
if (CollectionUtils.isEmpty(q.children)) {
435+
CSQueue queue = scheduler.getQueue(q.queueName);
436+
if (queue instanceof AbstractLeafQueue) {
437+
return ImmutableSet.of(q.queueName);
438+
}
439+
return Collections.emptySet();
439440
}
440441

441442
Set<String> leafQueueNames = new HashSet<>();

hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/capacity/AbstractParentQueue.java

+9
Original file line numberDiff line numberDiff line change
@@ -552,6 +552,15 @@ public boolean isEligibleForAutoQueueCreation() {
552552
return isDynamicQueue() || queueContext.getConfiguration().
553553
isAutoQueueCreationV2Enabled(getQueuePathObject());
554554
}
555+
/**
556+
* Check whether this queue supports legacy(v1) dynamic child queue creation.
557+
* @return true if queue is eligible to create child queues dynamically using
558+
* the legacy system, false otherwise
559+
*/
560+
public boolean isEligibleForLegacyAutoQueueCreation() {
561+
return isDynamicQueue() || queueContext.getConfiguration().
562+
isAutoCreateChildQueueEnabled(getQueuePathObject());
563+
}
555564

556565
@Override
557566
public void reinitialize(CSQueue newlyParsedQueue,

hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/monitor/capacity/TestProportionalCapacityPreemptionPolicy.java

+64-30
Original file line numberDiff line numberDiff line change
@@ -1083,44 +1083,74 @@ public void testRefreshPreemptionProperties() throws Exception {
10831083
}
10841084

10851085
@Test
1086-
public void testLeafQueueNameExtraction() throws Exception {
1087-
ProportionalCapacityPreemptionPolicy policy =
1088-
buildPolicy(Q_DATA_FOR_IGNORE);
1086+
public void testLeafQueueNameExtractionWithFlexibleAQC() throws Exception {
1087+
ProportionalCapacityPreemptionPolicy policy = buildPolicy(Q_DATA_FOR_IGNORE);
10891088
ParentQueue root = (ParentQueue) mCS.getRootQueue();
1089+
10901090
root.addDynamicParentQueue("childlessFlexible");
1091+
ParentQueue dynamicParent = setupDynamicParentQueue("root.dynamicParent", true);
1092+
extendRootQueueWithMock(root, dynamicParent);
1093+
1094+
policy.editSchedule();
1095+
assertFalse(policy.getLeafQueueNames().contains( "root.dynamicParent"),
1096+
"root.dynamicLegacyParent" + " should not be a LeafQueue candidate");
1097+
}
1098+
1099+
@Test
1100+
public void testLeafQueueNameExtractionWithLegacyAQC() throws Exception {
1101+
ProportionalCapacityPreemptionPolicy policy = buildPolicy(Q_DATA_FOR_IGNORE);
1102+
ParentQueue root = (ParentQueue) mCS.getRootQueue();
1103+
1104+
root.addDynamicParentQueue("childlessLegacy");
1105+
ParentQueue dynamicParent = setupDynamicParentQueue("root.dynamicLegacyParent", false);
1106+
extendRootQueueWithMock(root, dynamicParent);
1107+
1108+
policy.editSchedule();
1109+
assertFalse(policy.getLeafQueueNames().contains( "root.dynamicLegacyParent"),
1110+
"root.dynamicLegacyParent" + " should not be a LeafQueue candidate");
1111+
}
1112+
1113+
private ParentQueue setupDynamicParentQueue(String queuePath, boolean isFlexible) {
1114+
ParentQueue dynamicParent = mockParentQueue(null, 0, new LinkedList<>());
1115+
mockQueueFields(dynamicParent, queuePath);
1116+
1117+
if (isFlexible) {
1118+
when(dynamicParent.isEligibleForAutoQueueCreation()).thenReturn(true);
1119+
} else {
1120+
when(dynamicParent.isEligibleForLegacyAutoQueueCreation()).thenReturn(true);
1121+
}
1122+
1123+
return dynamicParent;
1124+
}
1125+
1126+
private void extendRootQueueWithMock(ParentQueue root, ParentQueue mockQueue) {
10911127
List<CSQueue> queues = root.getChildQueues();
10921128
ArrayList<CSQueue> extendedQueues = new ArrayList<>();
1093-
LinkedList<ParentQueue> pqs = new LinkedList<>();
1094-
ParentQueue dynamicParent = mockParentQueue(
1095-
null, 0, pqs);
1096-
when(dynamicParent.getQueuePath()).thenReturn("root.dynamicParent");
1097-
when(dynamicParent.getQueueCapacities()).thenReturn(
1098-
new QueueCapacities(false));
1099-
QueueResourceQuotas dynamicParentQr = new QueueResourceQuotas();
1100-
dynamicParentQr.setEffectiveMaxResource(Resource.newInstance(1, 1));
1101-
dynamicParentQr.setEffectiveMinResource(Resources.createResource(1));
1102-
dynamicParentQr.setEffectiveMaxResource(RMNodeLabelsManager.NO_LABEL,
1103-
Resource.newInstance(1, 1));
1104-
dynamicParentQr.setEffectiveMinResource(RMNodeLabelsManager.NO_LABEL,
1105-
Resources.createResource(1));
1106-
when(dynamicParent.getQueueResourceQuotas()).thenReturn(dynamicParentQr);
1107-
when(dynamicParent.getEffectiveCapacity(RMNodeLabelsManager.NO_LABEL))
1108-
.thenReturn(Resources.createResource(1));
1109-
when(dynamicParent.getEffectiveMaxCapacity(RMNodeLabelsManager.NO_LABEL))
1110-
.thenReturn(Resource.newInstance(1, 1));
1111-
ResourceUsage resUsage = new ResourceUsage();
1112-
resUsage.setUsed(Resources.createResource(1024));
1113-
resUsage.setReserved(Resources.createResource(1024));
1114-
when(dynamicParent.getQueueResourceUsage()).thenReturn(resUsage);
1115-
when(dynamicParent.isEligibleForAutoQueueCreation()).thenReturn(true);
1116-
extendedQueues.add(dynamicParent);
1129+
extendedQueues.add(mockQueue);
11171130
extendedQueues.addAll(queues);
11181131
when(root.getChildQueues()).thenReturn(extendedQueues);
1132+
}
11191133

1120-
policy.editSchedule();
1134+
private void mockQueueFields(ParentQueue queue, String queuePath) {
1135+
when(queue.getQueuePath()).thenReturn(queuePath);
1136+
when(queue.getQueueCapacities()).thenReturn(new QueueCapacities(false));
1137+
1138+
QueueResourceQuotas qrq = new QueueResourceQuotas();
1139+
qrq.setEffectiveMaxResource(Resource.newInstance(1, 1));
1140+
qrq.setEffectiveMinResource(Resources.createResource(1));
1141+
qrq.setEffectiveMaxResource(RMNodeLabelsManager.NO_LABEL, Resource.newInstance(1, 1));
1142+
qrq.setEffectiveMinResource(RMNodeLabelsManager.NO_LABEL, Resources.createResource(1));
11211143

1122-
assertFalse(policy.getLeafQueueNames().contains("root.dynamicParent"),
1123-
"dynamicParent should not be a LeafQueue candidate");
1144+
when(queue.getQueueResourceQuotas()).thenReturn(qrq);
1145+
when(queue.getEffectiveCapacity(RMNodeLabelsManager.NO_LABEL))
1146+
.thenReturn(Resources.createResource(1));
1147+
when(queue.getEffectiveMaxCapacity(RMNodeLabelsManager.NO_LABEL))
1148+
.thenReturn(Resource.newInstance(1, 1));
1149+
1150+
ResourceUsage usage = new ResourceUsage();
1151+
usage.setUsed(Resources.createResource(1024));
1152+
usage.setReserved(Resources.createResource(1024));
1153+
when(queue.getQueueResourceUsage()).thenReturn(usage);
11241154
}
11251155

11261156
static class IsPreemptionRequestFor
@@ -1369,6 +1399,10 @@ LeafQueue mockLeafQueue(ParentQueue p, Resource tot, int i, Resource[] abs,
13691399
Resource[] used, Resource[] pending, Resource[] reserved, int[] apps,
13701400
Resource[] gran) {
13711401
LeafQueue lq = mock(LeafQueue.class);
1402+
1403+
String queuePath = p.getQueuePath() + ".queue" + (char)('A' + i - 1);
1404+
when(mCS.getQueue(queuePath)).thenReturn(lq);
1405+
13721406
ResourceCalculator rc = mCS.getResourceCalculator();
13731407
List<ApplicationAttemptId> appAttemptIdList =
13741408
new ArrayList<ApplicationAttemptId>();

0 commit comments

Comments
 (0)