|
@@ -17,121 +17,76 @@
|
|
|
*/
|
|
|
package org.apache.hadoop.hdfs.server.namenode;
|
|
|
|
|
|
-import org.apache.hadoop.fs.permission.PermissionStatus;
|
|
|
import org.apache.hadoop.hdfs.protocol.DSQuotaExceededException;
|
|
|
import org.apache.hadoop.hdfs.protocol.HdfsConstants;
|
|
|
import org.apache.hadoop.hdfs.protocol.NSQuotaExceededException;
|
|
|
import org.apache.hadoop.hdfs.protocol.QuotaExceededException;
|
|
|
|
|
|
-import com.google.common.annotations.VisibleForTesting;
|
|
|
-
|
|
|
/**
|
|
|
- * Directory INode class that has a quota restriction
|
|
|
+ * Quota feature for {@link INodeDirectory}.
|
|
|
*/
|
|
|
-public class INodeDirectoryWithQuota extends INodeDirectory {
|
|
|
+public final class DirectoryWithQuotaFeature extends INodeDirectory.Feature {
|
|
|
+ public static final long DEFAULT_NAMESPACE_QUOTA = Long.MAX_VALUE;
|
|
|
+ public static final long DEFAULT_DISKSPACE_QUOTA = HdfsConstants.QUOTA_RESET;
|
|
|
+
|
|
|
/** Name space quota */
|
|
|
- private long nsQuota = Long.MAX_VALUE;
|
|
|
+ private long nsQuota = DEFAULT_NAMESPACE_QUOTA;
|
|
|
/** Name space count */
|
|
|
private long namespace = 1L;
|
|
|
/** Disk space quota */
|
|
|
- private long dsQuota = HdfsConstants.QUOTA_RESET;
|
|
|
+ private long dsQuota = DEFAULT_DISKSPACE_QUOTA;
|
|
|
/** Disk space count */
|
|
|
private long diskspace = 0L;
|
|
|
|
|
|
- /** Convert an existing directory inode to one with the given quota
|
|
|
- *
|
|
|
- * @param nsQuota Namespace quota to be assigned to this inode
|
|
|
- * @param dsQuota Diskspace quota to be assigned to this indoe
|
|
|
- * @param other The other inode from which all other properties are copied
|
|
|
- */
|
|
|
- INodeDirectoryWithQuota(INodeDirectory other, boolean adopt,
|
|
|
- long nsQuota, long dsQuota) {
|
|
|
- super(other, adopt);
|
|
|
- final Quota.Counts counts = other.computeQuotaUsage();
|
|
|
- this.namespace = counts.get(Quota.NAMESPACE);
|
|
|
- this.diskspace = counts.get(Quota.DISKSPACE);
|
|
|
+ DirectoryWithQuotaFeature(long nsQuota, long dsQuota) {
|
|
|
this.nsQuota = nsQuota;
|
|
|
this.dsQuota = dsQuota;
|
|
|
}
|
|
|
-
|
|
|
- public INodeDirectoryWithQuota(INodeDirectory other, boolean adopt,
|
|
|
- Quota.Counts quota) {
|
|
|
- this(other, adopt, quota.get(Quota.NAMESPACE), quota.get(Quota.DISKSPACE));
|
|
|
- }
|
|
|
|
|
|
- /** constructor with no quota verification */
|
|
|
- INodeDirectoryWithQuota(long id, byte[] name, PermissionStatus permissions,
|
|
|
- long modificationTime, long nsQuota, long dsQuota) {
|
|
|
- super(id, name, permissions, modificationTime);
|
|
|
- this.nsQuota = nsQuota;
|
|
|
- this.dsQuota = dsQuota;
|
|
|
- }
|
|
|
-
|
|
|
- /** constructor with no quota verification */
|
|
|
- INodeDirectoryWithQuota(long id, byte[] name, PermissionStatus permissions) {
|
|
|
- super(id, name, permissions, 0L);
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public Quota.Counts getQuotaCounts() {
|
|
|
+ /** @return the quota set or -1 if it is not set. */
|
|
|
+ Quota.Counts getQuota() {
|
|
|
return Quota.Counts.newInstance(nsQuota, dsQuota);
|
|
|
}
|
|
|
|
|
|
/** Set this directory's quota
|
|
|
*
|
|
|
* @param nsQuota Namespace quota to be set
|
|
|
- * @param dsQuota diskspace quota to be set
|
|
|
+ * @param dsQuota Diskspace quota to be set
|
|
|
*/
|
|
|
- public void setQuota(long nsQuota, long dsQuota) {
|
|
|
+ void setQuota(long nsQuota, long dsQuota) {
|
|
|
this.nsQuota = nsQuota;
|
|
|
this.dsQuota = dsQuota;
|
|
|
}
|
|
|
|
|
|
- @Override
|
|
|
- public Quota.Counts computeQuotaUsage(Quota.Counts counts, boolean useCache,
|
|
|
- int lastSnapshotId) {
|
|
|
- if (useCache && isQuotaSet()) {
|
|
|
- // use cache value
|
|
|
- counts.add(Quota.NAMESPACE, namespace);
|
|
|
- counts.add(Quota.DISKSPACE, diskspace);
|
|
|
- } else {
|
|
|
- super.computeQuotaUsage(counts, false, lastSnapshotId);
|
|
|
- }
|
|
|
+ Quota.Counts addNamespaceDiskspace(Quota.Counts counts) {
|
|
|
+ counts.add(Quota.NAMESPACE, namespace);
|
|
|
+ counts.add(Quota.DISKSPACE, diskspace);
|
|
|
return counts;
|
|
|
}
|
|
|
|
|
|
- @Override
|
|
|
- public ContentSummaryComputationContext computeContentSummary(
|
|
|
+ ContentSummaryComputationContext computeContentSummary(final INodeDirectory dir,
|
|
|
final ContentSummaryComputationContext summary) {
|
|
|
final long original = summary.getCounts().get(Content.DISKSPACE);
|
|
|
long oldYieldCount = summary.getYieldCount();
|
|
|
- super.computeContentSummary(summary);
|
|
|
+ dir.computeDirectoryContentSummary(summary);
|
|
|
// Check only when the content has not changed in the middle.
|
|
|
if (oldYieldCount == summary.getYieldCount()) {
|
|
|
- checkDiskspace(summary.getCounts().get(Content.DISKSPACE) - original);
|
|
|
+ checkDiskspace(dir, summary.getCounts().get(Content.DISKSPACE) - original);
|
|
|
}
|
|
|
return summary;
|
|
|
}
|
|
|
|
|
|
- private void checkDiskspace(final long computed) {
|
|
|
- if (-1 != getQuotaCounts().get(Quota.DISKSPACE) && diskspace != computed) {
|
|
|
+ private void checkDiskspace(final INodeDirectory dir, final long computed) {
|
|
|
+ if (-1 != getQuota().get(Quota.DISKSPACE) && diskspace != computed) {
|
|
|
NameNode.LOG.error("BUG: Inconsistent diskspace for directory "
|
|
|
- + getFullPathName() + ". Cached = " + diskspace
|
|
|
+ + dir.getFullPathName() + ". Cached = " + diskspace
|
|
|
+ " != Computed = " + computed);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- /** Get the number of names in the subtree rooted at this directory
|
|
|
- * @return the size of the subtree rooted at this directory
|
|
|
- */
|
|
|
- long numItemsInTree() {
|
|
|
- return namespace;
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public final void addSpaceConsumed(final long nsDelta, final long dsDelta,
|
|
|
- boolean verify) throws QuotaExceededException {
|
|
|
- if (isQuotaSet()) {
|
|
|
+ void addSpaceConsumed(final INodeDirectory dir, final long nsDelta,
|
|
|
+ final long dsDelta, boolean verify) throws QuotaExceededException {
|
|
|
+ if (dir.isQuotaSet()) {
|
|
|
// The following steps are important:
|
|
|
// check quotas in this inode and all ancestors before changing counts
|
|
|
// so that no change is made if there is any quota violation.
|
|
@@ -141,11 +96,11 @@ public class INodeDirectoryWithQuota extends INodeDirectory {
|
|
|
verifyQuota(nsDelta, dsDelta);
|
|
|
}
|
|
|
// (2) verify quota and then add count in ancestors
|
|
|
- super.addSpaceConsumed(nsDelta, dsDelta, verify);
|
|
|
+ dir.addSpaceConsumed2Parent(nsDelta, dsDelta, verify);
|
|
|
// (3) add count in this inode
|
|
|
addSpaceConsumed2Cache(nsDelta, dsDelta);
|
|
|
} else {
|
|
|
- super.addSpaceConsumed(nsDelta, dsDelta, verify);
|
|
|
+ dir.addSpaceConsumed2Parent(nsDelta, dsDelta, verify);
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -154,7 +109,7 @@ public class INodeDirectoryWithQuota extends INodeDirectory {
|
|
|
* @param nsDelta the change of the tree size
|
|
|
* @param dsDelta change to disk space occupied
|
|
|
*/
|
|
|
- protected void addSpaceConsumed2Cache(long nsDelta, long dsDelta) {
|
|
|
+ public void addSpaceConsumed2Cache(long nsDelta, long dsDelta) {
|
|
|
namespace += nsDelta;
|
|
|
diskspace += dsDelta;
|
|
|
}
|
|
@@ -172,41 +127,42 @@ public class INodeDirectoryWithQuota extends INodeDirectory {
|
|
|
this.diskspace = diskspace;
|
|
|
}
|
|
|
|
|
|
+ /** @return the namespace and diskspace consumed. */
|
|
|
+ public Quota.Counts getSpaceConsumed() {
|
|
|
+ return Quota.Counts.newInstance(namespace, diskspace);
|
|
|
+ }
|
|
|
+
|
|
|
/** Verify if the namespace quota is violated after applying delta. */
|
|
|
- void verifyNamespaceQuota(long delta) throws NSQuotaExceededException {
|
|
|
+ private void verifyNamespaceQuota(long delta) throws NSQuotaExceededException {
|
|
|
if (Quota.isViolated(nsQuota, namespace, delta)) {
|
|
|
throw new NSQuotaExceededException(nsQuota, namespace + delta);
|
|
|
}
|
|
|
}
|
|
|
+ /** Verify if the diskspace quota is violated after applying delta. */
|
|
|
+ private void verifyDiskspaceQuota(long delta) throws DSQuotaExceededException {
|
|
|
+ if (Quota.isViolated(dsQuota, diskspace, delta)) {
|
|
|
+ throw new DSQuotaExceededException(dsQuota, diskspace + delta);
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- /** Verify if the namespace count disk space satisfies the quota restriction
|
|
|
- * @throws QuotaExceededException if the given quota is less than the count
|
|
|
+ /**
|
|
|
+ * @throws QuotaExceededException if namespace or diskspace quotas is
|
|
|
+ * violated after applying the deltas.
|
|
|
*/
|
|
|
void verifyQuota(long nsDelta, long dsDelta) throws QuotaExceededException {
|
|
|
verifyNamespaceQuota(nsDelta);
|
|
|
-
|
|
|
- if (Quota.isViolated(dsQuota, diskspace, dsDelta)) {
|
|
|
- throw new DSQuotaExceededException(dsQuota, diskspace + dsDelta);
|
|
|
- }
|
|
|
+ verifyDiskspaceQuota(dsDelta);
|
|
|
}
|
|
|
|
|
|
- String namespaceString() {
|
|
|
+ private String namespaceString() {
|
|
|
return "namespace: " + (nsQuota < 0? "-": namespace + "/" + nsQuota);
|
|
|
}
|
|
|
- String diskspaceString() {
|
|
|
+ private String diskspaceString() {
|
|
|
return "diskspace: " + (dsQuota < 0? "-": diskspace + "/" + dsQuota);
|
|
|
}
|
|
|
- String quotaString() {
|
|
|
- return ", Quota[" + namespaceString() + ", " + diskspaceString() + "]";
|
|
|
- }
|
|
|
|
|
|
- @VisibleForTesting
|
|
|
- public long getNamespace() {
|
|
|
- return this.namespace;
|
|
|
- }
|
|
|
-
|
|
|
- @VisibleForTesting
|
|
|
- public long getDiskspace() {
|
|
|
- return this.diskspace;
|
|
|
+ @Override
|
|
|
+ public String toString() {
|
|
|
+ return "Quota[" + namespaceString() + ", " + diskspaceString() + "]";
|
|
|
}
|
|
|
}
|