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.hbase.quotas; 019 020import java.util.Objects; 021import org.apache.hadoop.hbase.TableName; 022import org.apache.yetus.audience.InterfaceAudience; 023import org.apache.yetus.audience.InterfaceStability; 024 025import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil; 026import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos.SetQuotaRequest; 027import org.apache.hadoop.hbase.shaded.protobuf.generated.QuotaProtos; 028import org.apache.hadoop.hbase.shaded.protobuf.generated.QuotaProtos.SpaceLimitRequest; 029import org.apache.hadoop.hbase.shaded.protobuf.generated.QuotaProtos.SpaceQuota; 030 031/** 032 * A {@link QuotaSettings} implementation for configuring filesystem-use quotas. 033 */ 034@InterfaceAudience.Private 035@InterfaceStability.Evolving 036class SpaceLimitSettings extends QuotaSettings { 037 038 private final SpaceLimitRequest proto; 039 040 SpaceLimitSettings(TableName tableName, long sizeLimit, SpaceViolationPolicy violationPolicy) { 041 super(null, Objects.requireNonNull(tableName), null, null); 042 validateSizeLimit(sizeLimit); 043 proto = buildProtoAddQuota(sizeLimit, Objects.requireNonNull(violationPolicy)); 044 } 045 046 /** 047 * Constructs a {@code SpaceLimitSettings} to remove a space quota on the given {@code tableName}. 048 */ 049 SpaceLimitSettings(TableName tableName) { 050 super(null, Objects.requireNonNull(tableName), null, null); 051 proto = buildProtoRemoveQuota(); 052 } 053 054 SpaceLimitSettings(String namespace, long sizeLimit, SpaceViolationPolicy violationPolicy) { 055 super(null, null, Objects.requireNonNull(namespace), null); 056 validateSizeLimit(sizeLimit); 057 proto = buildProtoAddQuota(sizeLimit, Objects.requireNonNull(violationPolicy)); 058 } 059 060 /** 061 * Constructs a {@code SpaceLimitSettings} to remove a space quota on the given {@code namespace}. 062 */ 063 SpaceLimitSettings(String namespace) { 064 super(null, null, Objects.requireNonNull(namespace), null); 065 proto = buildProtoRemoveQuota(); 066 } 067 068 SpaceLimitSettings(TableName tableName, String namespace, SpaceLimitRequest req) { 069 super(null, tableName, namespace, null); 070 proto = req; 071 } 072 073 /** 074 * Build a {@link SpaceLimitRequest} protobuf object from the given {@link SpaceQuota}. 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 * @param sizeLimit The size limit of the quota. 085 * @param violationPolicy The action to take when the quota is exceeded. 086 * @return The protobuf SpaceQuota representation. 087 */ 088 private SpaceLimitRequest buildProtoAddQuota(long sizeLimit, 089 SpaceViolationPolicy violationPolicy) { 090 return buildProtoFromQuota(SpaceQuota.newBuilder().setSoftLimit(sizeLimit) 091 .setViolationPolicy(ProtobufUtil.toProtoViolationPolicy(violationPolicy)).build()); 092 } 093 094 /** 095 * Builds a {@link SpaceQuota} protobuf object to remove a quota. 096 * @return The protobuf SpaceQuota representation. 097 */ 098 private SpaceLimitRequest buildProtoRemoveQuota() { 099 return SpaceLimitRequest.newBuilder().setQuota(SpaceQuota.newBuilder().setRemove(true).build()) 100 .build(); 101 } 102 103 /** 104 * Returns a copy of the internal state of <code>this</code> 105 */ 106 SpaceLimitRequest getProto() { 107 return proto.toBuilder().build(); 108 } 109 110 @Override 111 public QuotaType getQuotaType() { 112 return QuotaType.SPACE; 113 } 114 115 @Override 116 protected void setupSetQuotaRequest(SetQuotaRequest.Builder builder) { 117 // TableName/Namespace are serialized in QuotaSettings 118 builder.setSpaceLimit(proto); 119 } 120 121 /** 122 * Constructs a {@link SpaceLimitSettings} from the provided protobuf message and tablename. 123 * @param tableName The target tablename for the limit. 124 * @param proto The protobuf representation. 125 * @return A QuotaSettings. 126 */ 127 static SpaceLimitSettings fromSpaceQuota(final TableName tableName, 128 final QuotaProtos.SpaceQuota proto) { 129 validateProtoArguments(proto); 130 return new SpaceLimitSettings(tableName, proto.getSoftLimit(), 131 ProtobufUtil.toViolationPolicy(proto.getViolationPolicy())); 132 } 133 134 /** 135 * Constructs a {@link SpaceLimitSettings} from the provided protobuf message and namespace. 136 * @param namespace The target namespace for the limit. 137 * @param proto The protobuf representation. 138 * @return A QuotaSettings. 139 */ 140 static SpaceLimitSettings fromSpaceQuota(final String namespace, 141 final QuotaProtos.SpaceQuota proto) { 142 validateProtoArguments(proto); 143 return new SpaceLimitSettings(namespace, proto.getSoftLimit(), 144 ProtobufUtil.toViolationPolicy(proto.getViolationPolicy())); 145 } 146 147 /** 148 * Validates that the provided protobuf SpaceQuota has the necessary information to construct a 149 * {@link SpaceLimitSettings}. 150 * @param proto The protobuf message to validate. 151 */ 152 static void validateProtoArguments(final QuotaProtos.SpaceQuota proto) { 153 if (!Objects.requireNonNull(proto).hasSoftLimit()) { 154 throw new IllegalArgumentException("Cannot handle SpaceQuota without a soft limit"); 155 } 156 if (!proto.hasViolationPolicy()) { 157 throw new IllegalArgumentException("Cannot handle SpaceQuota without a violation policy"); 158 } 159 } 160 161 @Override 162 public int hashCode() { 163 return Objects.hash(getTableName(), getNamespace(), proto); 164 } 165 166 @Override 167 public boolean equals(Object o) { 168 if (o == this) { 169 return true; 170 } 171 if (!(o instanceof SpaceLimitSettings)) { 172 return false; 173 } 174 // o is non-null and an instance of SpaceLimitSettings 175 SpaceLimitSettings other = (SpaceLimitSettings) o; 176 return Objects.equals(getTableName(), other.getTableName()) 177 && Objects.equals(getNamespace(), other.getNamespace()) && Objects.equals(proto, other.proto); 178 } 179 180 @Override 181 public String toString() { 182 StringBuilder sb = new StringBuilder(); 183 sb.append("TYPE => SPACE"); 184 if (getTableName() != null) { 185 sb.append(", TABLE => ").append(getTableName()); 186 } 187 if (getNamespace() != null) { 188 sb.append(", NAMESPACE => ").append(getNamespace()); 189 } 190 if (proto.getQuota().getRemove()) { 191 sb.append(", REMOVE => ").append(proto.getQuota().getRemove()); 192 } else { 193 sb.append(", LIMIT => ").append(sizeToString(proto.getQuota().getSoftLimit())); 194 sb.append(", VIOLATION_POLICY => ").append(proto.getQuota().getViolationPolicy()); 195 } 196 return sb.toString(); 197 } 198 199 @Override 200 protected QuotaSettings merge(QuotaSettings newSettings) { 201 if (newSettings instanceof SpaceLimitSettings) { 202 SpaceLimitSettings settingsToMerge = (SpaceLimitSettings) newSettings; 203 204 // The message contained the expect SpaceQuota object 205 if (settingsToMerge.proto.hasQuota()) { 206 SpaceQuota quotaToMerge = settingsToMerge.proto.getQuota(); 207 if (quotaToMerge.getRemove()) { 208 return settingsToMerge; 209 } else { 210 // Validate that the two settings are for the same target. 211 // SpaceQuotas either apply to a table or a namespace (no user spacequota). 212 if ( 213 !Objects.equals(getTableName(), settingsToMerge.getTableName()) 214 && !Objects.equals(getNamespace(), settingsToMerge.getNamespace()) 215 ) { 216 throw new IllegalArgumentException("Cannot merge " + newSettings + " into " + this); 217 } 218 // Create a builder from the old settings 219 SpaceQuota.Builder mergedBuilder = this.proto.getQuota().toBuilder(); 220 // Build a new SpaceQuotas object from merging in the new settings 221 return new SpaceLimitSettings(getTableName(), getNamespace(), 222 buildProtoFromQuota(mergedBuilder.mergeFrom(quotaToMerge).build())); 223 } 224 } 225 // else, we don't know what to do, so return the original object 226 } 227 return this; 228 } 229 230 // Helper function to validate sizeLimit 231 private void validateSizeLimit(long sizeLimit) { 232 if (sizeLimit < 0L) { 233 throw new IllegalArgumentException("Size limit must be a non-negative value."); 234 } 235 } 236}