001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to you under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.hadoop.hbase.quotas; 018 019import java.util.Objects; 020 021import org.apache.hadoop.hbase.TableName; 022import org.apache.yetus.audience.InterfaceAudience; 023import org.apache.yetus.audience.InterfaceStability; 024import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil; 025import org.apache.hadoop.hbase.shaded.protobuf.generated.QuotaProtos; 026import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos.SetQuotaRequest.Builder; 027import org.apache.hadoop.hbase.shaded.protobuf.generated.QuotaProtos.SpaceLimitRequest; 028import org.apache.hadoop.hbase.shaded.protobuf.generated.QuotaProtos.SpaceQuota; 029 030/** 031 * A {@link QuotaSettings} implementation for configuring filesystem-use quotas. 032 */ 033@InterfaceAudience.Private 034@InterfaceStability.Evolving 035class SpaceLimitSettings extends QuotaSettings { 036 037 private final SpaceLimitRequest proto; 038 039 SpaceLimitSettings(TableName tableName, long sizeLimit, SpaceViolationPolicy violationPolicy) { 040 super(null, Objects.requireNonNull(tableName), null, null); 041 validateSizeLimit(sizeLimit); 042 proto = buildProtoAddQuota(sizeLimit, Objects.requireNonNull(violationPolicy)); 043 } 044 045 /** 046 * Constructs a {@code SpaceLimitSettings} to remove a space quota on the given {@code tableName}. 047 */ 048 SpaceLimitSettings(TableName tableName) { 049 super(null, Objects.requireNonNull(tableName), null, null); 050 proto = buildProtoRemoveQuota(); 051 } 052 053 SpaceLimitSettings(String namespace, long sizeLimit, SpaceViolationPolicy violationPolicy) { 054 super(null, null, Objects.requireNonNull(namespace), null); 055 validateSizeLimit(sizeLimit); 056 proto = buildProtoAddQuota(sizeLimit, Objects.requireNonNull(violationPolicy)); 057 } 058 059 /** 060 * Constructs a {@code SpaceLimitSettings} to remove a space quota on the given {@code namespace}. 061 */ 062 SpaceLimitSettings(String namespace) { 063 super(null, null, Objects.requireNonNull(namespace), null); 064 proto = buildProtoRemoveQuota(); 065 } 066 067 SpaceLimitSettings(TableName tableName, String namespace, SpaceLimitRequest req) { 068 super(null, tableName, namespace, null); 069 proto = req; 070 } 071 072 /** 073 * Build a {@link SpaceLimitRequest} protobuf object from the given {@link SpaceQuota}. 074 * 075 * @param protoQuota The preconstructed SpaceQuota protobuf 076 * @return A protobuf request to change a space limit quota 077 */ 078 private SpaceLimitRequest buildProtoFromQuota(SpaceQuota protoQuota) { 079 return SpaceLimitRequest.newBuilder().setQuota(protoQuota).build(); 080 } 081 082 /** 083 * Builds a {@link SpaceQuota} protobuf object given the arguments. 084 * 085 * @param sizeLimit The size limit of the quota. 086 * @param violationPolicy The action to take when the quota is exceeded. 087 * @return The protobuf SpaceQuota representation. 088 */ 089 private SpaceLimitRequest buildProtoAddQuota( 090 long sizeLimit, SpaceViolationPolicy violationPolicy) { 091 return buildProtoFromQuota(SpaceQuota.newBuilder() 092 .setSoftLimit(sizeLimit) 093 .setViolationPolicy(ProtobufUtil.toProtoViolationPolicy(violationPolicy)) 094 .build()); 095 } 096 097 /** 098 * Builds a {@link SpaceQuota} protobuf object to remove a quota. 099 * 100 * @return The protobuf SpaceQuota representation. 101 */ 102 private SpaceLimitRequest buildProtoRemoveQuota() { 103 return SpaceLimitRequest.newBuilder().setQuota( 104 SpaceQuota.newBuilder() 105 .setRemove(true) 106 .build()) 107 .build(); 108 } 109 110 /** 111 * Returns a copy of the internal state of <code>this</code> 112 */ 113 SpaceLimitRequest getProto() { 114 return proto.toBuilder().build(); 115 } 116 117 @Override 118 public QuotaType getQuotaType() { 119 return QuotaType.SPACE; 120 } 121 122 @Override 123 protected void setupSetQuotaRequest(Builder builder) { 124 // TableName/Namespace are serialized in QuotaSettings 125 builder.setSpaceLimit(proto); 126 } 127 128 /** 129 * Constructs a {@link SpaceLimitSettings} from the provided protobuf message and tablename. 130 * 131 * @param tableName The target tablename for the limit. 132 * @param proto The protobuf representation. 133 * @return A QuotaSettings. 134 */ 135 static SpaceLimitSettings fromSpaceQuota( 136 final TableName tableName, final QuotaProtos.SpaceQuota proto) { 137 validateProtoArguments(proto); 138 return new SpaceLimitSettings(tableName, proto.getSoftLimit(), 139 ProtobufUtil.toViolationPolicy(proto.getViolationPolicy())); 140 } 141 142 /** 143 * Constructs a {@link SpaceLimitSettings} from the provided protobuf message and namespace. 144 * 145 * @param namespace The target namespace for the limit. 146 * @param proto The protobuf representation. 147 * @return A QuotaSettings. 148 */ 149 static SpaceLimitSettings fromSpaceQuota( 150 final String namespace, final QuotaProtos.SpaceQuota proto) { 151 validateProtoArguments(proto); 152 return new SpaceLimitSettings(namespace, proto.getSoftLimit(), 153 ProtobufUtil.toViolationPolicy(proto.getViolationPolicy())); 154 } 155 156 /** 157 * Validates that the provided protobuf SpaceQuota has the necessary information to construct 158 * a {@link SpaceLimitSettings}. 159 * 160 * @param proto The protobuf message to validate. 161 */ 162 static void validateProtoArguments(final QuotaProtos.SpaceQuota proto) { 163 if (!Objects.requireNonNull(proto).hasSoftLimit()) { 164 throw new IllegalArgumentException("Cannot handle SpaceQuota without a soft limit"); 165 } 166 if (!proto.hasViolationPolicy()) { 167 throw new IllegalArgumentException("Cannot handle SpaceQuota without a violation policy"); 168 } 169 } 170 171 @Override 172 public int hashCode() { 173 return Objects.hash(getTableName(), getNamespace(), proto); 174 } 175 176 @Override 177 public boolean equals(Object o) { 178 if (o == this) { 179 return true; 180 } 181 if (!(o instanceof SpaceLimitSettings)) { 182 return false; 183 } 184 // o is non-null and an instance of SpaceLimitSettings 185 SpaceLimitSettings other = (SpaceLimitSettings) o; 186 return Objects.equals(getTableName(), other.getTableName()) && 187 Objects.equals(getNamespace(), other.getNamespace()) && 188 Objects.equals(proto, other.proto); 189 } 190 191 @Override 192 public String toString() { 193 StringBuilder sb = new StringBuilder(); 194 sb.append("TYPE => SPACE"); 195 if (getTableName() != null) { 196 sb.append(", TABLE => ").append(getTableName()); 197 } 198 if (getNamespace() != null) { 199 sb.append(", NAMESPACE => ").append(getNamespace()); 200 } 201 if (proto.getQuota().getRemove()) { 202 sb.append(", REMOVE => ").append(proto.getQuota().getRemove()); 203 } else { 204 sb.append(", LIMIT => ").append(sizeToString(proto.getQuota().getSoftLimit())); 205 sb.append(", VIOLATION_POLICY => ").append(proto.getQuota().getViolationPolicy()); 206 } 207 return sb.toString(); 208 } 209 210 @Override 211 protected QuotaSettings merge(QuotaSettings newSettings) { 212 if (newSettings instanceof SpaceLimitSettings) { 213 SpaceLimitSettings settingsToMerge = (SpaceLimitSettings) newSettings; 214 215 // The message contained the expect SpaceQuota object 216 if (settingsToMerge.proto.hasQuota()) { 217 SpaceQuota quotaToMerge = settingsToMerge.proto.getQuota(); 218 if (quotaToMerge.getRemove()) { 219 return settingsToMerge; 220 } else { 221 // Validate that the two settings are for the same target. 222 // SpaceQuotas either apply to a table or a namespace (no user spacequota). 223 if (!Objects.equals(getTableName(), settingsToMerge.getTableName()) 224 && !Objects.equals(getNamespace(), settingsToMerge.getNamespace())) { 225 throw new IllegalArgumentException("Cannot merge " + newSettings + " into " + this); 226 } 227 // Create a builder from the old settings 228 SpaceQuota.Builder mergedBuilder = this.proto.getQuota().toBuilder(); 229 // Build a new SpaceQuotas object from merging in the new settings 230 return new SpaceLimitSettings( 231 getTableName(), getNamespace(), 232 buildProtoFromQuota(mergedBuilder.mergeFrom(quotaToMerge).build())); 233 } 234 } 235 // else, we don't know what to do, so return the original object 236 } 237 return this; 238 } 239 240 // Helper function to validate sizeLimit 241 private void validateSizeLimit(long sizeLimit) { 242 if (sizeLimit < 0L) { 243 throw new IllegalArgumentException("Size limit must be a non-negative value."); 244 } 245 } 246}