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 */ 018package org.apache.hadoop.hdfs.server.namenode; 019 020import org.apache.hadoop.fs.permission.PermissionStatus; 021import org.apache.hadoop.hdfs.protocol.DSQuotaExceededException; 022import org.apache.hadoop.hdfs.protocol.HdfsConstants; 023import org.apache.hadoop.hdfs.protocol.NSQuotaExceededException; 024import org.apache.hadoop.hdfs.protocol.QuotaExceededException; 025 026import com.google.common.annotations.VisibleForTesting; 027 028/** 029 * Directory INode class that has a quota restriction 030 */ 031public class INodeDirectoryWithQuota extends INodeDirectory { 032 /** Name space quota */ 033 private long nsQuota = Long.MAX_VALUE; 034 /** Name space count */ 035 private long namespace = 1L; 036 /** Disk space quota */ 037 private long dsQuota = HdfsConstants.QUOTA_RESET; 038 /** Disk space count */ 039 private long diskspace = 0L; 040 041 /** Convert an existing directory inode to one with the given quota 042 * 043 * @param nsQuota Namespace quota to be assigned to this inode 044 * @param dsQuota Diskspace quota to be assigned to this indoe 045 * @param other The other inode from which all other properties are copied 046 */ 047 public INodeDirectoryWithQuota(INodeDirectory other, boolean adopt, 048 long nsQuota, long dsQuota) { 049 super(other, adopt); 050 final Quota.Counts counts = other.computeQuotaUsage(); 051 this.namespace = counts.get(Quota.NAMESPACE); 052 this.diskspace = counts.get(Quota.DISKSPACE); 053 this.nsQuota = nsQuota; 054 this.dsQuota = dsQuota; 055 } 056 057 /** constructor with no quota verification */ 058 INodeDirectoryWithQuota(long id, byte[] name, PermissionStatus permissions, 059 long modificationTime, long nsQuota, long dsQuota) { 060 super(id, name, permissions, modificationTime); 061 this.nsQuota = nsQuota; 062 this.dsQuota = dsQuota; 063 } 064 065 /** constructor with no quota verification */ 066 INodeDirectoryWithQuota(long id, byte[] name, PermissionStatus permissions) { 067 super(id, name, permissions, 0L); 068 } 069 070 /** Get this directory's namespace quota 071 * @return this directory's namespace quota 072 */ 073 @Override 074 public long getNsQuota() { 075 return nsQuota; 076 } 077 078 /** Get this directory's diskspace quota 079 * @return this directory's diskspace quota 080 */ 081 @Override 082 public long getDsQuota() { 083 return dsQuota; 084 } 085 086 /** Set this directory's quota 087 * 088 * @param nsQuota Namespace quota to be set 089 * @param dsQuota diskspace quota to be set 090 */ 091 public void setQuota(long nsQuota, long dsQuota) { 092 this.nsQuota = nsQuota; 093 this.dsQuota = dsQuota; 094 } 095 096 @Override 097 public Quota.Counts computeQuotaUsage(Quota.Counts counts, boolean useCache, 098 int lastSnapshotId) { 099 if (useCache && isQuotaSet()) { 100 // use cache value 101 counts.add(Quota.NAMESPACE, namespace); 102 counts.add(Quota.DISKSPACE, diskspace); 103 } else { 104 super.computeQuotaUsage(counts, false, lastSnapshotId); 105 } 106 return counts; 107 } 108 109 @Override 110 public ContentSummaryComputationContext computeContentSummary( 111 final ContentSummaryComputationContext summary) { 112 final long original = summary.getCounts().get(Content.DISKSPACE); 113 long oldYieldCount = summary.getYieldCount(); 114 super.computeContentSummary(summary); 115 // Check only when the content has not changed in the middle. 116 if (oldYieldCount == summary.getYieldCount()) { 117 checkDiskspace(summary.getCounts().get(Content.DISKSPACE) - original); 118 } 119 return summary; 120 } 121 122 private void checkDiskspace(final long computed) { 123 if (-1 != getDsQuota() && diskspace != computed) { 124 NameNode.LOG.error("BUG: Inconsistent diskspace for directory " 125 + getFullPathName() + ". Cached = " + diskspace 126 + " != Computed = " + computed); 127 } 128 } 129 130 /** Get the number of names in the subtree rooted at this directory 131 * @return the size of the subtree rooted at this directory 132 */ 133 long numItemsInTree() { 134 return namespace; 135 } 136 137 @Override 138 public final void addSpaceConsumed(final long nsDelta, final long dsDelta, 139 boolean verify) throws QuotaExceededException { 140 if (isQuotaSet()) { 141 // The following steps are important: 142 // check quotas in this inode and all ancestors before changing counts 143 // so that no change is made if there is any quota violation. 144 145 // (1) verify quota in this inode 146 if (verify) { 147 verifyQuota(nsDelta, dsDelta); 148 } 149 // (2) verify quota and then add count in ancestors 150 super.addSpaceConsumed(nsDelta, dsDelta, verify); 151 // (3) add count in this inode 152 addSpaceConsumed2Cache(nsDelta, dsDelta); 153 } else { 154 super.addSpaceConsumed(nsDelta, dsDelta, verify); 155 } 156 } 157 158 /** Update the size of the tree 159 * 160 * @param nsDelta the change of the tree size 161 * @param dsDelta change to disk space occupied 162 */ 163 protected void addSpaceConsumed2Cache(long nsDelta, long dsDelta) { 164 namespace += nsDelta; 165 diskspace += dsDelta; 166 } 167 168 /** 169 * Sets namespace and diskspace take by the directory rooted 170 * at this INode. This should be used carefully. It does not check 171 * for quota violations. 172 * 173 * @param namespace size of the directory to be set 174 * @param diskspace disk space take by all the nodes under this directory 175 */ 176 void setSpaceConsumed(long namespace, long diskspace) { 177 this.namespace = namespace; 178 this.diskspace = diskspace; 179 } 180 181 /** Verify if the namespace quota is violated after applying delta. */ 182 void verifyNamespaceQuota(long delta) throws NSQuotaExceededException { 183 if (Quota.isViolated(nsQuota, namespace, delta)) { 184 throw new NSQuotaExceededException(nsQuota, namespace + delta); 185 } 186 } 187 188 /** Verify if the namespace count disk space satisfies the quota restriction 189 * @throws QuotaExceededException if the given quota is less than the count 190 */ 191 void verifyQuota(long nsDelta, long dsDelta) throws QuotaExceededException { 192 verifyNamespaceQuota(nsDelta); 193 194 if (Quota.isViolated(dsQuota, diskspace, dsDelta)) { 195 throw new DSQuotaExceededException(dsQuota, diskspace + dsDelta); 196 } 197 } 198 199 String namespaceString() { 200 return "namespace: " + (nsQuota < 0? "-": namespace + "/" + nsQuota); 201 } 202 String diskspaceString() { 203 return "diskspace: " + (dsQuota < 0? "-": diskspace + "/" + dsQuota); 204 } 205 String quotaString() { 206 return ", Quota[" + namespaceString() + ", " + diskspaceString() + "]"; 207 } 208 209 @VisibleForTesting 210 public long getNamespace() { 211 return this.namespace; 212 } 213 214 @VisibleForTesting 215 public long getDiskspace() { 216 return this.diskspace; 217 } 218}