001 /**
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements. See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership. The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License. You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018 package org.apache.hadoop.hdfs.server.namenode;
019
020 import org.apache.hadoop.hdfs.protocol.DSQuotaExceededException;
021 import org.apache.hadoop.hdfs.protocol.HdfsConstants;
022 import org.apache.hadoop.hdfs.protocol.NSQuotaExceededException;
023 import org.apache.hadoop.hdfs.protocol.QuotaExceededException;
024
025 /**
026 * Quota feature for {@link INodeDirectory}.
027 */
028 public final class DirectoryWithQuotaFeature implements INode.Feature {
029 public static final long DEFAULT_NAMESPACE_QUOTA = Long.MAX_VALUE;
030 public static final long DEFAULT_DISKSPACE_QUOTA = HdfsConstants.QUOTA_RESET;
031
032 /** Name space quota */
033 private long nsQuota = DEFAULT_NAMESPACE_QUOTA;
034 /** Name space count */
035 private long namespace = 1L;
036 /** Disk space quota */
037 private long dsQuota = DEFAULT_DISKSPACE_QUOTA;
038 /** Disk space count */
039 private long diskspace = 0L;
040
041 DirectoryWithQuotaFeature(long nsQuota, long dsQuota) {
042 this.nsQuota = nsQuota;
043 this.dsQuota = dsQuota;
044 }
045
046 /** @return the quota set or -1 if it is not set. */
047 Quota.Counts getQuota() {
048 return Quota.Counts.newInstance(nsQuota, dsQuota);
049 }
050
051 /** Set this directory's quota
052 *
053 * @param nsQuota Namespace quota to be set
054 * @param dsQuota Diskspace quota to be set
055 */
056 void setQuota(long nsQuota, long dsQuota) {
057 this.nsQuota = nsQuota;
058 this.dsQuota = dsQuota;
059 }
060
061 Quota.Counts addNamespaceDiskspace(Quota.Counts counts) {
062 counts.add(Quota.NAMESPACE, namespace);
063 counts.add(Quota.DISKSPACE, diskspace);
064 return counts;
065 }
066
067 ContentSummaryComputationContext computeContentSummary(final INodeDirectory dir,
068 final ContentSummaryComputationContext summary) {
069 final long original = summary.getCounts().get(Content.DISKSPACE);
070 long oldYieldCount = summary.getYieldCount();
071 dir.computeDirectoryContentSummary(summary);
072 // Check only when the content has not changed in the middle.
073 if (oldYieldCount == summary.getYieldCount()) {
074 checkDiskspace(dir, summary.getCounts().get(Content.DISKSPACE) - original);
075 }
076 return summary;
077 }
078
079 private void checkDiskspace(final INodeDirectory dir, final long computed) {
080 if (-1 != getQuota().get(Quota.DISKSPACE) && diskspace != computed) {
081 NameNode.LOG.error("BUG: Inconsistent diskspace for directory "
082 + dir.getFullPathName() + ". Cached = " + diskspace
083 + " != Computed = " + computed);
084 }
085 }
086
087 void addSpaceConsumed(final INodeDirectory dir, final long nsDelta,
088 final long dsDelta, boolean verify) throws QuotaExceededException {
089 if (dir.isQuotaSet()) {
090 // The following steps are important:
091 // check quotas in this inode and all ancestors before changing counts
092 // so that no change is made if there is any quota violation.
093
094 // (1) verify quota in this inode
095 if (verify) {
096 verifyQuota(nsDelta, dsDelta);
097 }
098 // (2) verify quota and then add count in ancestors
099 dir.addSpaceConsumed2Parent(nsDelta, dsDelta, verify);
100 // (3) add count in this inode
101 addSpaceConsumed2Cache(nsDelta, dsDelta);
102 } else {
103 dir.addSpaceConsumed2Parent(nsDelta, dsDelta, verify);
104 }
105 }
106
107 /** Update the size of the tree
108 *
109 * @param nsDelta the change of the tree size
110 * @param dsDelta change to disk space occupied
111 */
112 public void addSpaceConsumed2Cache(long nsDelta, long dsDelta) {
113 namespace += nsDelta;
114 diskspace += dsDelta;
115 }
116
117 /**
118 * Sets namespace and diskspace take by the directory rooted
119 * at this INode. This should be used carefully. It does not check
120 * for quota violations.
121 *
122 * @param namespace size of the directory to be set
123 * @param diskspace disk space take by all the nodes under this directory
124 */
125 void setSpaceConsumed(long namespace, long diskspace) {
126 this.namespace = namespace;
127 this.diskspace = diskspace;
128 }
129
130 /** @return the namespace and diskspace consumed. */
131 public Quota.Counts getSpaceConsumed() {
132 return Quota.Counts.newInstance(namespace, diskspace);
133 }
134
135 /** Verify if the namespace quota is violated after applying delta. */
136 private void verifyNamespaceQuota(long delta) throws NSQuotaExceededException {
137 if (Quota.isViolated(nsQuota, namespace, delta)) {
138 throw new NSQuotaExceededException(nsQuota, namespace + delta);
139 }
140 }
141 /** Verify if the diskspace quota is violated after applying delta. */
142 private void verifyDiskspaceQuota(long delta) throws DSQuotaExceededException {
143 if (Quota.isViolated(dsQuota, diskspace, delta)) {
144 throw new DSQuotaExceededException(dsQuota, diskspace + delta);
145 }
146 }
147
148 /**
149 * @throws QuotaExceededException if namespace or diskspace quotas is
150 * violated after applying the deltas.
151 */
152 void verifyQuota(long nsDelta, long dsDelta) throws QuotaExceededException {
153 verifyNamespaceQuota(nsDelta);
154 verifyDiskspaceQuota(dsDelta);
155 }
156
157 boolean isQuotaSet() {
158 return nsQuota >= 0 || dsQuota >= 0;
159 }
160
161 private String namespaceString() {
162 return "namespace: " + (nsQuota < 0? "-": namespace + "/" + nsQuota);
163 }
164 private String diskspaceString() {
165 return "diskspace: " + (dsQuota < 0? "-": diskspace + "/" + dsQuota);
166 }
167
168 @Override
169 public String toString() {
170 return "Quota[" + namespaceString() + ", " + diskspaceString() + "]";
171 }
172 }