| /** |
| * 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.hdfs.server.namenode; |
| |
| import org.apache.hadoop.thirdparty.com.google.common.annotations.VisibleForTesting; |
| import org.apache.hadoop.fs.StorageType; |
| import org.apache.hadoop.hdfs.protocol.HdfsConstants; |
| import org.apache.hadoop.hdfs.util.ConstEnumCounters; |
| import org.apache.hadoop.hdfs.util.EnumCounters; |
| |
| import java.util.function.Consumer; |
| |
| /** |
| * Counters for namespace, storage space and storage type space quota and usage. |
| */ |
| public class QuotaCounts { |
| |
| /** |
| * We pre-define 4 most common used EnumCounters objects. When the nsSsCounts |
| * and tsCounts are set to the 4 most common used value, we just point them to |
| * the pre-defined const EnumCounters objects instead of constructing many |
| * objects with the same value. See HDFS-14547. |
| */ |
| final static EnumCounters<Quota> QUOTA_RESET = |
| new ConstEnumCounters<>(Quota.class, HdfsConstants.QUOTA_RESET); |
| final static EnumCounters<Quota> QUOTA_DEFAULT = |
| new ConstEnumCounters<>(Quota.class, 0); |
| final static EnumCounters<StorageType> STORAGE_TYPE_RESET = |
| new ConstEnumCounters<>(StorageType.class, HdfsConstants.QUOTA_RESET); |
| final static EnumCounters<StorageType> STORAGE_TYPE_DEFAULT = |
| new ConstEnumCounters<>(StorageType.class, 0); |
| |
| /** |
| * Modify counter with action. If the counter is ConstEnumCounters, copy all |
| * the values of it to a new EnumCounters object, and modify the new obj. |
| * |
| * @param counter the EnumCounters to be modified. |
| * @param action the modifying action on counter. |
| * @return the modified counter. |
| */ |
| static <T extends Enum<T>> EnumCounters<T> modify(EnumCounters<T> counter, |
| Consumer<EnumCounters<T>> action) { |
| if (counter instanceof ConstEnumCounters) { |
| counter = counter.deepCopyEnumCounter(); |
| } |
| action.accept(counter); |
| return counter; |
| } |
| |
| // Name space and storage space counts (HDFS-7775 refactors the original disk |
| // space count to storage space counts) |
| @VisibleForTesting |
| EnumCounters<Quota> nsSsCounts; |
| // Storage type space counts |
| @VisibleForTesting |
| EnumCounters<StorageType> tsCounts; |
| |
| public static class Builder { |
| private EnumCounters<Quota> nsSsCounts; |
| private EnumCounters<StorageType> tsCounts; |
| |
| public Builder() { |
| this.nsSsCounts = QUOTA_DEFAULT; |
| this.tsCounts = STORAGE_TYPE_DEFAULT; |
| } |
| |
| public Builder nameSpace(long val) { |
| nsSsCounts = |
| setQuotaCounter(nsSsCounts, Quota.NAMESPACE, Quota.STORAGESPACE, val); |
| return this; |
| } |
| |
| public Builder storageSpace(long val) { |
| nsSsCounts = |
| setQuotaCounter(nsSsCounts, Quota.STORAGESPACE, Quota.NAMESPACE, val); |
| return this; |
| } |
| |
| public Builder typeSpaces(EnumCounters<StorageType> val) { |
| if (val != null) { |
| if (val == STORAGE_TYPE_DEFAULT || val == STORAGE_TYPE_RESET) { |
| tsCounts = val; |
| } else { |
| tsCounts = modify(tsCounts, ec -> ec.set(val)); |
| } |
| } |
| return this; |
| } |
| |
| public Builder typeSpaces(long val) { |
| if (val == HdfsConstants.QUOTA_RESET) { |
| tsCounts = STORAGE_TYPE_RESET; |
| } else if (val == 0) { |
| tsCounts = STORAGE_TYPE_DEFAULT; |
| } else { |
| tsCounts = modify(tsCounts, ec -> ec.reset(val)); |
| } |
| return this; |
| } |
| |
| public Builder quotaCount(QuotaCounts that) { |
| if (that.nsSsCounts == QUOTA_DEFAULT || that.nsSsCounts == QUOTA_RESET) { |
| nsSsCounts = that.nsSsCounts; |
| } else { |
| nsSsCounts = modify(nsSsCounts, ec -> ec.set(that.nsSsCounts)); |
| } |
| if (that.tsCounts == STORAGE_TYPE_DEFAULT |
| || that.tsCounts == STORAGE_TYPE_RESET) { |
| tsCounts = that.tsCounts; |
| } else { |
| tsCounts = modify(tsCounts, ec -> ec.set(that.tsCounts)); |
| } |
| return this; |
| } |
| |
| public QuotaCounts build() { |
| return new QuotaCounts(this); |
| } |
| } |
| |
| private QuotaCounts(Builder builder) { |
| this.nsSsCounts = builder.nsSsCounts; |
| this.tsCounts = builder.tsCounts; |
| } |
| |
| public QuotaCounts add(QuotaCounts that) { |
| nsSsCounts = modify(nsSsCounts, ec -> ec.add(that.nsSsCounts)); |
| tsCounts = modify(tsCounts, ec -> ec.add(that.tsCounts)); |
| return this; |
| } |
| |
| public QuotaCounts subtract(QuotaCounts that) { |
| nsSsCounts = modify(nsSsCounts, ec -> ec.subtract(that.nsSsCounts)); |
| tsCounts = modify(tsCounts, ec -> ec.subtract(that.tsCounts)); |
| return this; |
| } |
| |
| /** |
| * Returns a QuotaCounts whose value is {@code (-this)}. |
| * |
| * @return {@code -this} |
| */ |
| public QuotaCounts negation() { |
| QuotaCounts ret = new QuotaCounts.Builder().quotaCount(this).build(); |
| ret.nsSsCounts = modify(ret.nsSsCounts, ec -> ec.negation()); |
| ret.tsCounts = modify(ret.tsCounts, ec -> ec.negation()); |
| return ret; |
| } |
| |
| public long getNameSpace(){ |
| return nsSsCounts.get(Quota.NAMESPACE); |
| } |
| |
| public void setNameSpace(long nameSpaceCount) { |
| nsSsCounts = |
| setQuotaCounter(nsSsCounts, Quota.NAMESPACE, Quota.STORAGESPACE, |
| nameSpaceCount); |
| } |
| |
| public void addNameSpace(long nsDelta) { |
| nsSsCounts = modify(nsSsCounts, ec -> ec.add(Quota.NAMESPACE, nsDelta)); |
| } |
| |
| public long getStorageSpace(){ |
| return nsSsCounts.get(Quota.STORAGESPACE); |
| } |
| |
| public void setStorageSpace(long spaceCount) { |
| nsSsCounts = |
| setQuotaCounter(nsSsCounts, Quota.STORAGESPACE, Quota.NAMESPACE, |
| spaceCount); |
| } |
| |
| public void addStorageSpace(long dsDelta) { |
| nsSsCounts = modify(nsSsCounts, ec -> ec.add(Quota.STORAGESPACE, dsDelta)); |
| } |
| |
| public EnumCounters<StorageType> getTypeSpaces() { |
| EnumCounters<StorageType> ret = |
| new EnumCounters<StorageType>(StorageType.class); |
| ret.set(tsCounts); |
| return ret; |
| } |
| |
| void setTypeSpaces(EnumCounters<StorageType> that) { |
| if (that == STORAGE_TYPE_DEFAULT || that == STORAGE_TYPE_RESET) { |
| tsCounts = that; |
| } else if (that != null) { |
| tsCounts = modify(tsCounts, ec -> ec.set(that)); |
| } |
| } |
| |
| long getTypeSpace(StorageType type) { |
| return this.tsCounts.get(type); |
| } |
| |
| void setTypeSpace(StorageType type, long spaceCount) { |
| tsCounts = modify(tsCounts, ec -> ec.set(type, spaceCount)); |
| } |
| |
| public void addTypeSpace(StorageType type, long delta) { |
| tsCounts = modify(tsCounts, ec -> ec.add(type, delta)); |
| } |
| |
| public boolean anyNsSsCountGreaterOrEqual(long val) { |
| if (nsSsCounts == QUOTA_DEFAULT) { |
| return val <= 0; |
| } else if (nsSsCounts == QUOTA_RESET) { |
| return val <= HdfsConstants.QUOTA_RESET; |
| } |
| return nsSsCounts.anyGreaterOrEqual(val); |
| } |
| |
| public boolean anyTypeSpaceCountGreaterOrEqual(long val) { |
| if (tsCounts == STORAGE_TYPE_DEFAULT) { |
| return val <= 0; |
| } else if (tsCounts == STORAGE_TYPE_RESET) { |
| return val <= HdfsConstants.QUOTA_RESET; |
| } |
| return tsCounts.anyGreaterOrEqual(val); |
| } |
| |
| /** |
| * Set inputCounts' value of Quota type quotaToSet to val. |
| * inputCounts should be the left side value of this method. |
| * |
| * @param inputCounts the EnumCounters instance. |
| * @param quotaToSet the quota type to be set. |
| * @param otherQuota the other quota type besides quotaToSet. |
| * @param val the value to be set. |
| * @return the modified inputCounts. |
| */ |
| private static EnumCounters<Quota> setQuotaCounter( |
| EnumCounters<Quota> inputCounts, Quota quotaToSet, Quota otherQuota, |
| long val) { |
| if (val == HdfsConstants.QUOTA_RESET |
| && inputCounts.get(otherQuota) == HdfsConstants.QUOTA_RESET) { |
| return QUOTA_RESET; |
| } else if (val == 0 && inputCounts.get(otherQuota) == 0) { |
| return QUOTA_DEFAULT; |
| } else { |
| return modify(inputCounts, ec -> ec.set(quotaToSet, val)); |
| } |
| } |
| |
| @Override |
| public String toString() { |
| return "name space=" + getNameSpace() + |
| "\nstorage space=" + getStorageSpace() + |
| "\nstorage types=" + getTypeSpaces(); |
| } |
| |
| @Override |
| public boolean equals(Object obj) { |
| if (obj == this) { |
| return true; |
| } else if (!(obj instanceof QuotaCounts)) { |
| return false; |
| } |
| final QuotaCounts that = (QuotaCounts)obj; |
| return this.nsSsCounts.equals(that.nsSsCounts) |
| && this.tsCounts.equals(that.tsCounts); |
| } |
| |
| @Override |
| public int hashCode() { |
| assert false : "hashCode not designed"; |
| return 42; // any arbitrary constant will do |
| } |
| |
| } |