diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/util/StateDump.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/util/StateDump.java new file mode 100644 index 00000000000..cb75ee4e60b --- /dev/null +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/util/StateDump.java @@ -0,0 +1,158 @@ +/** + * 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.util; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +/** + * Provides a convenient way to collect and dump internal states + * of entities. Consists of a collection of blocks. + */ +public class StateDump { + + private static final String START = "{"; + private static final String END = "}"; + private static final String BLOCK_INTERNAL_DELIMITER = ", "; + private static final String BLOCK_DELIMITER = ", "; + private static final String FORMAT = "%s: %s"; + + private List blocks; + private StateDumpBlock currentBlock; + + public StateDump() { + this.blocks = new ArrayList<>(); + } + + public static class StateDumpBlock { + private String title; + private Map states; + + public StateDumpBlock(String blockTitle) { + this.title = blockTitle; + this.states = new HashMap<>(); + } + + public StateDumpBlock addState(String stateName, Object stateValue) { + states.put(stateName, stateValue != null ? stateValue.toString() : ""); + return this; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public Map getStates() { + return states; + } + + } + + /** + * Starts a new state block. A block groups states under a block title. + * @param blockTitle Name of the block + * @return this {@link StateDump} + */ + public StateDump startBlock(String blockTitle) { + if (currentBlock != null) { + throw new IllegalStateException("Can not start a new block without " + + "closing the existing one."); + } + + currentBlock = new StateDumpBlock(blockTitle); + return this; + } + + /** + * Adds a new state to the currently unclosed block. + * @param stateName Name of state + * @param stateValue Value of state + * @throws IllegalStateException if a block is not open + * @return this {@link StateDump} + */ + public StateDump addState(String stateName, Object stateValue) { + if (currentBlock == null) { + throw new IllegalStateException("No block has been started."); + } + + currentBlock.addState(stateName, stateValue); + return this; + } + + + /** + * Ends the initialized block. + * @throws IllegalStateException if a block is already closed + * @return this {@link StateDump} + */ + public StateDump endBlock() { + if (currentBlock == null) { + throw new IllegalStateException("No block has been started."); + } + + blocks.add(currentBlock); + currentBlock = null; + return this; + } + + /** + * Adds a new block. + * @param stateDumpBlock State block + */ + public void addBlock(StateDumpBlock stateDumpBlock) { + blocks.add(stateDumpBlock); + } + + /** + * Creates a string representation of the collected states. + * @return Collected states + */ + public String dump() { + StringBuilder sb = new StringBuilder(); + for (Iterator it = blocks.iterator(); it.hasNext();) { + StateDumpBlock block = it.next(); + sb.append(convertBlock(block)); + if (it.hasNext()) { + sb.append(BLOCK_DELIMITER); + } + } + + return sb.toString(); + } + + private String convertBlock(StateDumpBlock block) { + String title = convertState("Title", block.getTitle()); + String states = block.getStates().entrySet().stream() + .map(e -> convertState(e.getKey(), e.getValue())) + .reduce((s1, s2) -> s1 + BLOCK_INTERNAL_DELIMITER + s2).orElse(""); + + return START + title + BLOCK_INTERNAL_DELIMITER + states + END; + } + + private String convertState(String stateName, Object stateValue) { + return String.format(FORMAT, stateName, stateValue); + } + +} 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/AbstractCSQueue.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/AbstractCSQueue.java index 0a4a14f063e..9c4b39501c8 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/AbstractCSQueue.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/AbstractCSQueue.java @@ -31,6 +31,7 @@ import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.apache.hadoop.yarn.util.StateDump; import org.apache.hadoop.classification.InterfaceAudience.Private; import org.apache.hadoop.ipc.Server; import org.apache.hadoop.security.AccessControlException; @@ -1431,4 +1432,20 @@ public long getDefaultApplicationLifetime() { public boolean getDefaultAppLifetimeWasSpecifiedInConfig() { return defaultAppLifetimeWasSpecifiedInConfig; } + + @Override + public void dumpState(StateDump stateDump) { + stateDump + .addState("Name", getQueueName()) + .addState("State", getState().name()) + .addState("AbsoluteCapacity", getAbsoluteCapacity()) + .addState("AbsoluteMaxCapacity", getAbsoluteMaximumCapacity()) + .addState("AbsoluteUsedCapacity", getAbsoluteUsedCapacity()) + .addState("Capacity", getCapacity()) + .addState("MaxCapacity", getMaximumCapacity()) + .addState("UsedCapacity", getUsedCapacity()) + .addState("ApplicationNumber", getNumApplications()) + .addState("ResourceUsage", getQueueResourceUsage()) + .addState("Containers", getNumContainers()); + } } 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/CSQueue.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/CSQueue.java index 1937573e4d4..c197bd29f52 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/CSQueue.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/CSQueue.java @@ -52,6 +52,7 @@ import org.apache.hadoop.yarn.server.resourcemanager.scheduler.placement.CandidateNodeSet; import com.google.common.annotations.VisibleForTesting; +import org.apache.hadoop.yarn.util.StateDump; /** * CSQueue represents a node in the tree of @@ -477,4 +478,11 @@ public void validateSubmitApplication(ApplicationId applicationId, * @return indicator whether set or calculated */ boolean getDefaultAppLifetimeWasSpecifiedInConfig(); + + /** + * Dumps the internal state of the queue and any additional queues in the + * hierarchy. + * @param stateDump Provided state dump helper + */ + void dumpState(StateDump stateDump); } 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/CapacityScheduler.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/CapacityScheduler.java index 5cef57adac6..2d370f7a252 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/CapacityScheduler.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/CapacityScheduler.java @@ -35,6 +35,7 @@ import java.util.concurrent.atomic.AtomicBoolean; import org.apache.commons.lang3.StringUtils; +import org.apache.hadoop.yarn.util.StateDump; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.slf4j.Marker; @@ -180,6 +181,8 @@ MarkerFactory.getMarker("FATAL"); private static final Logger LOG = LoggerFactory.getLogger(CapacityScheduler.class); + private static final Logger STATE_DUMP_LOG = + LoggerFactory.getLogger(CapacityScheduler.class.getName() + ".statedump"); private CapacitySchedulerQueueManager queueManager; @@ -3271,4 +3274,19 @@ public boolean isMultiNodePlacementEnabled() { public int getNumAsyncSchedulerThreads() { return asyncSchedulerThreads == null ? 0 : asyncSchedulerThreads.size(); } + + /** + * Dumps the internal state of the scheduler and its queues. + * @return Current state data of the scheduler + */ + public StateDump dumpState() { + StateDump stateDump = new StateDump(); + getRootQueue().dumpState(stateDump); + stateDump.startBlock("CapacityScheduler") + .addState("LastNodeUpdate", this.lastNodeUpdateTime) + .addState("LastAllocatedContainer", this.getSchedulerHealth() + .getLastAllocationDetails().getContainerId()) + .endBlock(); + return stateDump; + } } 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/LeafQueue.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/LeafQueue.java index 9f0caf291ea..896271c17a1 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/LeafQueue.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/LeafQueue.java @@ -27,6 +27,7 @@ import org.apache.commons.lang3.time.DateUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.apache.hadoop.yarn.util.StateDump; import org.apache.hadoop.classification.InterfaceAudience.Private; import org.apache.hadoop.classification.InterfaceStability.Unstable; import org.apache.hadoop.security.AccessControlException; @@ -2229,4 +2230,11 @@ private void updateQueuePreemptionMetrics(RMContainer rmc) { usedSeconds); metrics.updatePreemptedForCustomResources(containerResource); } + + @Override + public void dumpState(StateDump stateDump) { + stateDump.startBlock("ChildQueue"); + super.dumpState(stateDump); + stateDump.endBlock(); + } } 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 95f5468ebaf..8fb61660356 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 @@ -28,6 +28,7 @@ import java.util.Set; import org.apache.commons.lang3.StringUtils; +import org.apache.hadoop.yarn.util.StateDump; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.apache.hadoop.classification.InterfaceAudience.Private; @@ -1383,4 +1384,18 @@ public void stopQueue() { public QueueOrderingPolicy getQueueOrderingPolicy() { return queueOrderingPolicy; } + + @Override + public void dumpState(StateDump stateDump) { + stateDump.startBlock("ParentQueue"); + super.dumpState(stateDump); + stateDump + .addState("Policy", getQueueOrderingPolicyConfigName()) + .endBlock(); + + for (CSQueue child : getChildQueues()) { + child.dumpState(stateDump); + } + + } } 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/TestCapacityScheduler.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/TestCapacityScheduler.java index a746f06f274..63322457683 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/TestCapacityScheduler.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/TestCapacityScheduler.java @@ -54,6 +54,7 @@ import com.google.common.collect.Sets; import org.apache.hadoop.service.ServiceStateException; +import org.apache.hadoop.yarn.util.StateDump; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.apache.hadoop.conf.Configuration; @@ -6027,4 +6028,26 @@ public void testReservedContainerLeakWhenMoveApplication() throws Exception { Assert.assertEquals(0, desQueue.getUsedResources().getMemorySize()); rm1.close(); } + + @Test + public void testStateDump() { + String nameTemplate = "Name: %s"; + String[] queues = {"root", "a", "a1", "a2", "b", "b1", "b2", "b3"}; + + CapacityScheduler scheduler = new CapacityScheduler(); + scheduler.setConf(new YarnConfiguration()); + scheduler.setRMContext(resourceManager.getRMContext()); + CapacitySchedulerConfiguration conf = new CapacitySchedulerConfiguration(); + + setupQueueConfiguration(conf); + + scheduler.init(conf); + scheduler.start(); + StateDump stateDump = scheduler.dumpState(); + String dumpStateOutput = stateDump.dump(); + + for (String queue : queues) { + assertTrue(dumpStateOutput.contains(String.format(nameTemplate, queue))); + } + } }