Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,26 @@ private void updateQueueCapacities(QueueCapacities queueCapacities) {
}
}

/**
* Recomputes leaf queue template capacities in ABSOLUTE_RESOURCE mode.
* This handles the case where a queue is recreated during RM recovery
* before any NodeManager has registered, causing the initial capacity
* to be computed as 0. The periodic queue management policy recalculates
* the template once cluster resources become available. No operation for other
* queue modes.
*/
Comment on lines +265 to +272
public void updateTemplateCapacitiesForAbsoluteResource() {
writeLock.lock();
try {
if (this.capacityConfigType.equals(
CapacityConfigType.ABSOLUTE_RESOURCE)) {
updateQueueCapacities(getLeafQueueTemplate().getQueueCapacities());
}
} finally {
writeLock.unlock();
}
}

protected void validate(final CSQueue newlyParsedQueue) throws IOException {
// Sanity check
if (!(newlyParsedQueue instanceof ManagedParentQueue) || !newlyParsedQueue
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.ReentrantReadWriteLock;

import static org.apache.hadoop.yarn.server.resourcemanager.scheduler
.capacity.AbstractCSQueue.CapacityConfigType.ABSOLUTE_RESOURCE;
import static org.apache.hadoop.yarn.server.resourcemanager.scheduler
.capacity.CSQueueUtils.EPSILON;

Expand Down Expand Up @@ -310,6 +312,15 @@ private void initializeLeafQueueTemplate(ManagedParentQueue parentQueue)
public List<QueueManagementChange> computeQueueManagementChanges()
throws SchedulerDynamicEditException {

// Recompute the leaf queue template capacities from the current cluster
// resource for ABSOLUTE_RESOURCE mode. The template fraction is otherwise
// computed only at reinitialize; if a leaf queue was auto-created
// while the cluster resource was zero during RM recovery before any
// NodeManager registered the fraction stays 0 and the queue never regains
// capacity. This refresh lets the template pick up the real capacity once
// NodeManagers register. No operation unless it is ABSOLUTE_RESOURCE.
managedParentQueue.updateTemplateCapacitiesForAbsoluteResource();

// Update template absolute capacities as the capacities could have changed
// in weight mode
updateTemplateAbsoluteCapacities(managedParentQueue.getQueueCapacities(),
Expand Down Expand Up @@ -716,8 +727,18 @@ public AutoCreatedLeafQueueConfig getInitialLeafQueueConfiguration(
getAbsoluteCapacity(nodeLabel) - parentQueueState.
getAbsoluteActivatedChildQueueCapacity(nodeLabel) + EPSILON;

if (availableCapacity >= leafQueueTemplateCapacities
.getAbsoluteCapacity(nodeLabel)) {
// In ABSOLUTE_RESOURCE mode, don't activate a leaf queue while the cluster
// resource is 0 during RM recovery before NodeManagers register.
// Otherwise, it gets initialized with zero capacity and never updates.
// Create it deactivated instead, and let the monitor activate it once
// cluster resources are available. Percentage and weight modes are unaffected.
boolean deferActivationUntilClusterResource =
managedParentQueue.getCapacityConfigType() == ABSOLUTE_RESOURCE
&& managedParentQueue.getQueueContext().getClusterResource()
.getMemorySize() <= 0;

if (!deferActivationUntilClusterResource && availableCapacity
>= leafQueueTemplateCapacities.getAbsoluteCapacity(nodeLabel)) {
updateCapacityFromTemplate(capacities, nodeLabel);
activate(leafQueue, nodeLabel);
} else{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import static org.apache.hadoop.yarn.nodelabels.CommonNodeLabelsManager.NO_LABEL;
import static org.apache.hadoop.yarn.server.resourcemanager.scheduler.capacity.CSQueueUtils.EPSILON;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
Expand Down Expand Up @@ -338,4 +339,63 @@ public void testApplicationRunningWithDRF() throws Exception {
ManagedParentQueue parentQueue = (ManagedParentQueue) cs.getQueue(QUEUED);
assertEquals(parentQueue, autoCreatedLeafQueue.getParent());
}

@Test
@Timeout(value = 20)
public void testAutoCreatedLeafQueueRecoversCapacityAfterNodeRegisters()
throws Exception {
try {
CapacitySchedulerConfiguration csConf =
setupSimpleQueueConfiguration(false);
setupMinMaxResourceConfiguration(csConf);
csConf.setClass(YarnConfiguration.RM_SCHEDULER, CapacityScheduler.class,
ResourceScheduler.class);
csConf.setOverrideWithQueueMappings(true);
setupGroupQueueMappings(QUEUED, csConf, "%user");

mockRM = new MockRM(csConf);
cs = (CapacityScheduler) mockRM.getResourceScheduler();
mockRM.start();
cs.start();

submitApp(mockRM, cs.getQueue(QUEUED), TEST_GROUPUSER, TEST_GROUPUSER, 1,
1);

AutoCreatedLeafQueue autoCreatedLeafQueue =
(AutoCreatedLeafQueue) cs.getQueue(TEST_GROUPUSER);
ManagedParentQueue parentQueue = (ManagedParentQueue) cs.getQueue(QUEUED);
assertNotNull(autoCreatedLeafQueue, "Auto Creation of Queue failed");
assertEquals(parentQueue, autoCreatedLeafQueue.getParent());

GuaranteedOrZeroCapacityOverTimePolicy policy =
(GuaranteedOrZeroCapacityOverTimePolicy) parentQueue
.getAutoCreatedQueueManagementPolicy();

// With no cluster resource the queue must be created at zero capacity and
// must NOT be activated - activating it here is exactly what strands it at
// zero capacity for the lifetime of the queue.
assertEquals(0.0f, autoCreatedLeafQueue.getCapacity(), EPSILON);
assertEquals(0.0f, autoCreatedLeafQueue.getAbsoluteCapacity(), EPSILON);
assertFalse(policy.isActive(autoCreatedLeafQueue, NO_LABEL),
"Queue must not be activated while the cluster resource is zero");

// A NodeManager registers: the cluster resource becomes available.
mockRM.registerNode("127.0.0.1:1234", 250 * GB, 40);
mockRM.drainEvents();

// The queue management monitor runs, which is what
// QueueManagementDynamicEditPolicy does on its monitoring interval.
List<QueueManagementChange> changes =
policy.computeQueueManagementChanges();
parentQueue.validateAndApplyQueueManagementChanges(changes);

validateCapacities(autoCreatedLeafQueue, 0.4f, 0.04f, 1f, 0.6f);
assertTrue(policy.isActive(autoCreatedLeafQueue, NO_LABEL),
"Queue must be activated once the cluster resource is available");
assertEquals(0.04f,
policy.getAbsoluteActivatedChildQueueCapacity(NO_LABEL), EPSILON);
} finally {
cleanupQueue(TEST_GROUPUSER);
}
}
}
Loading