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.hdfs.protocol.DSQuotaExceededException; 021import org.apache.hadoop.hdfs.protocol.HdfsConstants; 022import org.apache.hadoop.hdfs.protocol.NSQuotaExceededException; 023import org.apache.hadoop.hdfs.protocol.QuotaExceededException; 024 025/** 026 * Quota feature for {@link INodeDirectory}. 027 */ 028public 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}