blob: ab4b7420f92c6198c72d179c5af448a125c348a1 [file] [log] [blame]
/**
* 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;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
import org.apache.hadoop.yarn.api.records.Resource;
import org.apache.hadoop.yarn.nodelabels.CommonNodeLabelsManager;
import org.apache.hadoop.yarn.util.resource.Resources;
/**
* Resource Usage by Labels for following fields by label - AM resource (to
* enforce max-am-resource-by-label after YARN-2637) - Used resource (includes
* AM resource usage) - Reserved resource - Pending resource - Headroom
*
* This class can be used to track resource usage in queue/user/app.
*
* And it is thread-safe
*/
public class ResourceUsage {
private ReadLock readLock;
private WriteLock writeLock;
private Map<String, UsageByLabel> usages;
// short for no-label :)
private static final String NL = CommonNodeLabelsManager.NO_LABEL;
private final UsageByLabel usageNoLabel;
public ResourceUsage() {
ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
readLock = lock.readLock();
writeLock = lock.writeLock();
usages = new HashMap<String, UsageByLabel>();
usageNoLabel = new UsageByLabel(NL);
usages.put(NL, usageNoLabel);
}
// Usage enum here to make implement cleaner
private enum ResourceType {
//CACHED_USED and CACHED_PENDING may be read by anyone, but must only
//be written by ordering policies
USED(0), PENDING(1), AMUSED(2), RESERVED(3), CACHED_USED(4),
CACHED_PENDING(5), AMLIMIT(6), USERAMLIMIT(7);
private int idx;
private ResourceType(int value) {
this.idx = value;
}
}
private static class UsageByLabel {
// usage by label, contains all UsageType
private Resource[] resArr;
public UsageByLabel(String label) {
resArr = new Resource[ResourceType.values().length];
for (int i = 0; i < resArr.length; i++) {
resArr[i] = Resource.newInstance(0, 0);
};
}
public Resource getUsed() {
return resArr[ResourceType.USED.idx];
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("{used=" + resArr[0] + "%, ");
sb.append("pending=" + resArr[1] + "%, ");
sb.append("am_used=" + resArr[2] + "%, ");
sb.append("reserved=" + resArr[3] + "%}");
sb.append("am_limit=" + resArr[6] + "%, ");
return sb.toString();
}
}
/*
* Used
*/
public Resource getUsed() {
return getUsed(NL);
}
public Resource getUsed(String label) {
return _get(label, ResourceType.USED);
}
public Resource getCachedUsed() {
return _get(NL, ResourceType.CACHED_USED);
}
public Resource getCachedUsed(String label) {
return _get(label, ResourceType.CACHED_USED);
}
public Resource getCachedPending() {
return _get(NL, ResourceType.CACHED_PENDING);
}
public Resource getCachedPending(String label) {
return _get(label, ResourceType.CACHED_PENDING);
}
public void incUsed(String label, Resource res) {
_inc(label, ResourceType.USED, res);
}
public void incUsed(Resource res) {
incUsed(NL, res);
}
public void decUsed(Resource res) {
decUsed(NL, res);
}
public void decUsed(String label, Resource res) {
_dec(label, ResourceType.USED, res);
}
public void setUsed(Resource res) {
setUsed(NL, res);
}
public void copyAllUsed(ResourceUsage other) {
try {
writeLock.lock();
for (Entry<String, UsageByLabel> entry : other.usages.entrySet()) {
setUsed(entry.getKey(), Resources.clone(entry.getValue().getUsed()));
}
} finally {
writeLock.unlock();
}
}
public void setUsed(String label, Resource res) {
_set(label, ResourceType.USED, res);
}
public void setCachedUsed(String label, Resource res) {
_set(label, ResourceType.CACHED_USED, res);
}
public void setCachedUsed(Resource res) {
_set(NL, ResourceType.CACHED_USED, res);
}
public void setCachedPending(String label, Resource res) {
_set(label, ResourceType.CACHED_PENDING, res);
}
public void setCachedPending(Resource res) {
_set(NL, ResourceType.CACHED_PENDING, res);
}
/*
* Pending
*/
public Resource getPending() {
return getPending(NL);
}
public Resource getPending(String label) {
return _get(label, ResourceType.PENDING);
}
public void incPending(String label, Resource res) {
_inc(label, ResourceType.PENDING, res);
}
public void incPending(Resource res) {
incPending(NL, res);
}
public void decPending(Resource res) {
decPending(NL, res);
}
public void decPending(String label, Resource res) {
_dec(label, ResourceType.PENDING, res);
}
public void setPending(Resource res) {
setPending(NL, res);
}
public void setPending(String label, Resource res) {
_set(label, ResourceType.PENDING, res);
}
/*
* Reserved
*/
public Resource getReserved() {
return getReserved(NL);
}
public Resource getReserved(String label) {
return _get(label, ResourceType.RESERVED);
}
public void incReserved(String label, Resource res) {
_inc(label, ResourceType.RESERVED, res);
}
public void incReserved(Resource res) {
incReserved(NL, res);
}
public void decReserved(Resource res) {
decReserved(NL, res);
}
public void decReserved(String label, Resource res) {
_dec(label, ResourceType.RESERVED, res);
}
public void setReserved(Resource res) {
setReserved(NL, res);
}
public void setReserved(String label, Resource res) {
_set(label, ResourceType.RESERVED, res);
}
/*
* AM-Used
*/
public Resource getAMUsed() {
return getAMUsed(NL);
}
public Resource getAMUsed(String label) {
return _get(label, ResourceType.AMUSED);
}
public void incAMUsed(String label, Resource res) {
_inc(label, ResourceType.AMUSED, res);
}
public void incAMUsed(Resource res) {
incAMUsed(NL, res);
}
public void decAMUsed(Resource res) {
decAMUsed(NL, res);
}
public void decAMUsed(String label, Resource res) {
_dec(label, ResourceType.AMUSED, res);
}
public void setAMUsed(Resource res) {
setAMUsed(NL, res);
}
public void setAMUsed(String label, Resource res) {
_set(label, ResourceType.AMUSED, res);
}
/*
* AM-Resource Limit
*/
public Resource getAMLimit() {
return getAMLimit(NL);
}
public Resource getAMLimit(String label) {
return _get(label, ResourceType.AMLIMIT);
}
public void incAMLimit(String label, Resource res) {
_inc(label, ResourceType.AMLIMIT, res);
}
public void incAMLimit(Resource res) {
incAMLimit(NL, res);
}
public void decAMLimit(Resource res) {
decAMLimit(NL, res);
}
public void decAMLimit(String label, Resource res) {
_dec(label, ResourceType.AMLIMIT, res);
}
public void setAMLimit(Resource res) {
setAMLimit(NL, res);
}
public void setAMLimit(String label, Resource res) {
_set(label, ResourceType.AMLIMIT, res);
}
private static Resource normalize(Resource res) {
if (res == null) {
return Resources.none();
}
return res;
}
private Resource _get(String label, ResourceType type) {
if (label == null || label.equals(NL)) {
return normalize(usageNoLabel.resArr[type.idx]);
}
try {
readLock.lock();
UsageByLabel usage = usages.get(label);
if (null == usage) {
return Resources.none();
}
return normalize(usage.resArr[type.idx]);
} finally {
readLock.unlock();
}
}
private Resource _getAll(ResourceType type) {
try {
readLock.lock();
Resource allOfType = Resources.createResource(0);
for (Map.Entry<String, UsageByLabel> usageEntry : usages.entrySet()) {
//all usages types are initialized
Resources.addTo(allOfType, usageEntry.getValue().resArr[type.idx]);
}
return allOfType;
} finally {
readLock.unlock();
}
}
public Resource getAllPending() {
return _getAll(ResourceType.PENDING);
}
public Resource getAllUsed() {
return _getAll(ResourceType.USED);
}
private UsageByLabel getAndAddIfMissing(String label) {
if (label == null || label.equals(NL)) {
return usageNoLabel;
}
if (!usages.containsKey(label)) {
UsageByLabel u = new UsageByLabel(label);
usages.put(label, u);
return u;
}
return usages.get(label);
}
private void _set(String label, ResourceType type, Resource res) {
try {
writeLock.lock();
UsageByLabel usage = getAndAddIfMissing(label);
usage.resArr[type.idx] = res;
} finally {
writeLock.unlock();
}
}
private void _inc(String label, ResourceType type, Resource res) {
try {
writeLock.lock();
UsageByLabel usage = getAndAddIfMissing(label);
Resources.addTo(usage.resArr[type.idx], res);
} finally {
writeLock.unlock();
}
}
private void _dec(String label, ResourceType type, Resource res) {
try {
writeLock.lock();
UsageByLabel usage = getAndAddIfMissing(label);
Resources.subtractFrom(usage.resArr[type.idx], res);
} finally {
writeLock.unlock();
}
}
public Resource getCachedDemand(String label) {
try {
readLock.lock();
Resource demand = Resources.createResource(0);
Resources.addTo(demand, getCachedUsed(label));
Resources.addTo(demand, getCachedPending(label));
return demand;
} finally {
readLock.unlock();
}
}
@Override
public String toString() {
try {
readLock.lock();
return usages.toString();
} finally {
readLock.unlock();
}
}
public Set<String> getNodePartitionsSet() {
try {
readLock.lock();
return usages.keySet();
} finally {
readLock.unlock();
}
}
public Resource getUserAMLimit() {
return getAMLimit(NL);
}
public Resource getUserAMLimit(String label) {
return _get(label, ResourceType.USERAMLIMIT);
}
public void setUserAMLimit(Resource res) {
setAMLimit(NL, res);
}
public void setUserAMLimit(String label, Resource res) {
_set(label, ResourceType.USERAMLIMIT, res);
}
}