diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/capacity/AutoCreatedQueueTemplateConfig.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/capacity/AutoCreatedQueueTemplateConfig.java new file mode 100644 index 00000000000..89f81a8ef02 --- /dev/null +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/capacity/AutoCreatedQueueTemplateConfig.java @@ -0,0 +1,108 @@ +/** + * 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.scheduler.capacity; + +import org.apache.hadoop.conf.Configuration; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Represents a configuration that handles template entries based on its + * parent setting. + */ +public class AutoCreatedQueueTemplateConfig + extends CapacitySchedulerConfiguration { + public static final String AUTO_QUEUE_TEMPLATE_PREFIX = PREFIX + + AUTO_QUEUE_CREATION_V2_PREFIX + ".template"; + private static final String WILDCARD_QUEUE = "*"; + private static final int MAX_WILDCARD_LEVEL = 2; + + private final String queuePath; + + public AutoCreatedQueueTemplateConfig(Configuration configuration, + String queuePath) { + super(configuration); + this.queuePath = queuePath; + setTemplateConfigEntries(); + } + + /** + * Set the template configuration entries. Explicit templates always take + * precedence over wildcard values. An example template precedence + * hierarchy of root.a.b.c from highest to lowest: + * yarn.scheduler.capacity.auto-queue-creation-v2.template.root.a.b.capacity + * yarn.scheduler.capacity.auto-queue-creation-v2.template.root.a.*.capacity + * yarn.scheduler.capacity.auto-queue-creation-v2.template.root.*.*.capacity + * + */ + private void setTemplateConfigEntries() { + HashMap templateEntries = new HashMap<>(); + List queuePathParts = new ArrayList<>(Arrays.asList( + queuePath.split("\\."))); + + if (queuePathParts.size() <= 1) { + // This is either root or an empty queue name + return; + } else { + // Only dealing with parents, thus removing the LeafQueue part + queuePathParts.remove(queuePathParts.size() - 1); + } + int queueHierarchyParts = queuePathParts.size(); + + // start with the most explicit format (without wildcard) + int wildcardLevel = 0; + // root can not be wildcarded + int supportedWildcardLevel = Math.min(queueHierarchyParts - 1, + MAX_WILDCARD_LEVEL); + + while (wildcardLevel <= supportedWildcardLevel) { + // Get all config entries with the specified prefix + String templateQueuePath = String.join(".", queuePathParts); + Map props = getPropsWithPrefix( + getAutoQueueTemplatePrefix(templateQueuePath)); + for (Map.Entry entry : props.entrySet()) { + templateEntries.putIfAbsent(entry.getKey(), entry.getValue()); + } + + for (int i = 0; i <= wildcardLevel; ++i) { + queuePathParts.set(queuePathParts.size() - 1 - i, WILDCARD_QUEUE); + } + + ++wildcardLevel; + } + + setConfigFromTemplateEntries(templateEntries); + } + + private void setConfigFromTemplateEntries( + HashMap templateEntries) { + for (Map.Entry entry : templateEntries.entrySet()) { + set(PREFIX + queuePath + entry.getKey(), entry.getValue()); + } + } + + private String getAutoQueueTemplatePrefix(String queue) { + return AUTO_QUEUE_TEMPLATE_PREFIX + DOT + queue; + } + +} diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/capacity/CapacitySchedulerConfiguration.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/capacity/CapacitySchedulerConfiguration.java index abbc2d7875f..e2dc041c866 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/capacity/CapacitySchedulerConfiguration.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/capacity/CapacitySchedulerConfiguration.java @@ -2009,7 +2009,7 @@ public void setDefaultLifetimePerQueue(String queue, long defaultLifetime) { AUTO_CREATE_CHILD_QUEUE_PREFIX + "enabled"; @Private - private static final String AUTO_QUEUE_CREATION_V2_PREFIX = + protected static final String AUTO_QUEUE_CREATION_V2_PREFIX = "auto-queue-creation-v2"; @Private diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/capacity/ParentQueue.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/capacity/ParentQueue.java index 0a2f0820070..e81330e5a7d 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/capacity/ParentQueue.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/capacity/ParentQueue.java @@ -475,8 +475,8 @@ private CapacitySchedulerConfiguration getConfForAutoCreatedQueue( String childQueuePath, boolean isLeaf) { // Copy existing config CapacitySchedulerConfiguration dupCSConfig = - new CapacitySchedulerConfiguration( - csContext.getConfiguration(), false); + new AutoCreatedQueueTemplateConfig(csContext.getConfiguration(), + childQueuePath); if (isLeaf) { // FIXME: Ideally we should disable user limit factor, see YARN-10531 // dupCSConfig.setUserLimitFactor(childQueuePath, ); @@ -1184,7 +1184,9 @@ public void updateClusterResource(Resource clusterResource, // For dynamic queue, we will set weight to 1 every time, because it // is possible new labels added to the parent. if (((AbstractCSQueue) queue).isDynamicQueue()) { - queue.getQueueCapacities().setWeight(nodeLabel, 1f); + if (queue.getQueueCapacities().getWeight(nodeLabel) == -1f) { + queue.getQueueCapacities().setWeight(nodeLabel, 1f); + } } } } diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/capacity/TestAutoCreatedQueueTemplateConfig.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/capacity/TestAutoCreatedQueueTemplateConfig.java new file mode 100644 index 00000000000..7166723e2f5 --- /dev/null +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/capacity/TestAutoCreatedQueueTemplateConfig.java @@ -0,0 +1,122 @@ +/** + * 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.scheduler.capacity; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +public class TestAutoCreatedQueueTemplateConfig { + private static final String TEST_QUEUE_ABC = "root.a.b.c"; + private static final String TEST_QUEUE_AB = "root.a.b"; + private static final String TEST_QUEUE_A = "root.a"; + private static final String ROOT = "root"; + private CapacitySchedulerConfiguration conf; + + @Before + public void setUp() throws Exception { + conf = new CapacitySchedulerConfiguration(); + conf.setQueues("root", new String[]{"a"}); + conf.setQueues("a", new String[]{"b"}); + conf.setQueues("b", new String[]{"c"}); + } + + @Test + public void testNonWildCardTemplate() { + conf.set(getTemplateKey(TEST_QUEUE_AB, "capacity"), "6w"); + AutoCreatedQueueTemplateConfig templateConfig = + new AutoCreatedQueueTemplateConfig(conf, TEST_QUEUE_ABC); + + Assert.assertEquals("weight is not set", 6f, + templateConfig.getNonLabeledQueueWeight(TEST_QUEUE_ABC), 10e-6); + + } + + @Test + public void testOneLevelWildcardTemplate() { + conf.set(getTemplateKey("root.a.*", "capacity"), "6w"); + AutoCreatedQueueTemplateConfig templateConfig = + new AutoCreatedQueueTemplateConfig(conf, TEST_QUEUE_ABC); + + Assert.assertEquals("weight is not set", 6f, + templateConfig.getNonLabeledQueueWeight(TEST_QUEUE_ABC), 10e-6); + + } + + @Test + public void testTwoLevelsWildcardTemplate() { + conf.set(getTemplateKey("root.*.*", "capacity"), "6w"); + AutoCreatedQueueTemplateConfig templateConfig = + new AutoCreatedQueueTemplateConfig(conf, TEST_QUEUE_ABC); + + Assert.assertEquals("weight is not set", 6f, + templateConfig.getNonLabeledQueueWeight(TEST_QUEUE_ABC), 10e-6); + } + + @Test + public void testIgnoredWhenRootWildcarded() { + conf.set(getTemplateKey("*", "capacity"), "6w"); + AutoCreatedQueueTemplateConfig templateConfig = + new AutoCreatedQueueTemplateConfig(conf, TEST_QUEUE_A); + + Assert.assertEquals("weight is set", -1f, + templateConfig.getNonLabeledQueueWeight(TEST_QUEUE_A), 10e-6); + } + + @Test + public void testIgnoredWhenNoParent() { + conf.set(getTemplateKey("root", "capacity"), "6w"); + AutoCreatedQueueTemplateConfig templateConfig = + new AutoCreatedQueueTemplateConfig(conf, ROOT); + + Assert.assertEquals("weight is set", -1f, + templateConfig.getNonLabeledQueueWeight(ROOT), 10e-6); + } + + @Test + public void testTemplatePrecedence() { + conf.set(getTemplateKey("root.a.b", "capacity"), "6w"); + conf.set(getTemplateKey("root.a.*", "capacity"), "4w"); + conf.set(getTemplateKey("root.*.*", "capacity"), "2w"); + + AutoCreatedQueueTemplateConfig templateConfig = + new AutoCreatedQueueTemplateConfig(conf, TEST_QUEUE_ABC); + + Assert.assertEquals( + "explicit template does not have the highest precedence", 6f, + templateConfig.getNonLabeledQueueWeight(TEST_QUEUE_ABC), 10e-6); + + CapacitySchedulerConfiguration newConf = + new CapacitySchedulerConfiguration(); + newConf.set(getTemplateKey("root.a.*", "capacity"), "4w"); + newConf.set(getTemplateKey("root.*.*", "capacity"), "2w"); + + AutoCreatedQueueTemplateConfig newTemplateConfig = + new AutoCreatedQueueTemplateConfig(newConf, TEST_QUEUE_ABC); + + Assert.assertEquals("fewer wildcard does not have higher " + + "precedence than more wildcard", 4f, + newTemplateConfig.getNonLabeledQueueWeight(TEST_QUEUE_ABC), 10e-6); + } + + private String getTemplateKey(String queuePath, String entryKey) { + return AutoCreatedQueueTemplateConfig.AUTO_QUEUE_TEMPLATE_PREFIX + "." + + queuePath + "." + entryKey; + } +} \ No newline at end of file diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/capacity/TestCapacitySchedulerNewQueueAutoCreation.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/capacity/TestCapacitySchedulerNewQueueAutoCreation.java index 25b2f4d0c4a..c7d06a25462 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/capacity/TestCapacitySchedulerNewQueueAutoCreation.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/capacity/TestCapacitySchedulerNewQueueAutoCreation.java @@ -423,6 +423,18 @@ public void testAutoQueueCreationOnAppSubmission() throws Exception { Assert.assertTrue(user0.isDynamicQueue()); } + @Test + public void testAutoCreatedQueueTemplateConfig() throws Exception { + startScheduler(); + csConf.set(AutoCreatedQueueTemplateConfig.AUTO_QUEUE_TEMPLATE_PREFIX + + "." + "root.a.*.capacity", "6w"); + cs.reinitialize(csConf, mockRM.getRMContext()); + + LeafQueue a2 = createQueue("root.a.a-auto.a2"); + Assert.assertEquals("weight is not set", 6f, + a2.getQueueCapacities().getWeight(), 1e-6); + } + private LeafQueue createQueue(String queuePath) throws YarnException { return autoQueueHandler.autoCreateQueue( CSQueueUtils.extractQueuePath(queuePath));