2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to you under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
17 package org
.apache
.hadoop
.hbase
.quotas
;
19 import java
.io
.IOException
;
20 import java
.util
.ArrayList
;
21 import java
.util
.HashMap
;
22 import java
.util
.List
;
24 import java
.util
.Objects
;
25 import java
.util
.Map
.Entry
;
27 import org
.apache
.hadoop
.hbase
.DoNotRetryIOException
;
28 import org
.apache
.hadoop
.hbase
.TableName
;
29 import org
.apache
.hadoop
.hbase
.quotas
.QuotaSettingsFactory
.QuotaGlobalsSettingsBypass
;
30 import org
.apache
.yetus
.audience
.InterfaceAudience
;
32 import org
.apache
.hadoop
.hbase
.shaded
.protobuf
.ProtobufUtil
;
33 import org
.apache
.hadoop
.hbase
.shaded
.protobuf
.generated
.QuotaProtos
;
34 import org
.apache
.hadoop
.hbase
.shaded
.protobuf
.generated
.QuotaProtos
.Quotas
;
35 import org
.apache
.hadoop
.hbase
.shaded
.protobuf
.generated
.QuotaProtos
.SpaceQuota
;
36 import org
.apache
.hadoop
.hbase
.shaded
.protobuf
.generated
.QuotaProtos
.Throttle
;
37 import org
.apache
.hadoop
.hbase
.shaded
.protobuf
.generated
.QuotaProtos
.TimedQuota
;
40 * Implementation of {@link GlobalQuotaSettings} to hide the Protobuf messages we use internally.
42 @InterfaceAudience.Private
43 public class GlobalQuotaSettingsImpl
extends GlobalQuotaSettings
{
45 private final QuotaProtos
.Throttle throttleProto
;
46 private final Boolean bypassGlobals
;
47 private final QuotaProtos
.SpaceQuota spaceProto
;
49 protected GlobalQuotaSettingsImpl(String username
, TableName tableName
, String namespace
,
50 String regionServer
, QuotaProtos
.Quotas quotas
) {
51 this(username
, tableName
, namespace
, regionServer
,
52 (quotas
!= null && quotas
.hasThrottle() ? quotas
.getThrottle() : null),
53 (quotas
!= null && quotas
.hasBypassGlobals() ? quotas
.getBypassGlobals() : null),
54 (quotas
!= null && quotas
.hasSpace() ? quotas
.getSpace() : null));
57 protected GlobalQuotaSettingsImpl(String userName
, TableName tableName
, String namespace
,
58 String regionServer
, QuotaProtos
.Throttle throttleProto
, Boolean bypassGlobals
,
59 QuotaProtos
.SpaceQuota spaceProto
) {
60 super(userName
, tableName
, namespace
, regionServer
);
61 this.throttleProto
= throttleProto
;
62 this.bypassGlobals
= bypassGlobals
;
63 this.spaceProto
= spaceProto
;
67 public List
<QuotaSettings
> getQuotaSettings() {
68 // Very similar to QuotaSettingsFactory
69 List
<QuotaSettings
> settings
= new ArrayList
<>();
70 if (throttleProto
!= null) {
71 settings
.addAll(QuotaSettingsFactory
.fromThrottle(getUserName(), getTableName(),
72 getNamespace(), getRegionServer(), throttleProto
));
74 if (bypassGlobals
!= null && bypassGlobals
.booleanValue()) {
75 settings
.add(new QuotaGlobalsSettingsBypass(getUserName(), getTableName(), getNamespace(),
76 getRegionServer(), true));
78 if (spaceProto
!= null) {
79 settings
.add(QuotaSettingsFactory
.fromSpace(getTableName(), getNamespace(), spaceProto
));
84 protected QuotaProtos
.Throttle
getThrottleProto() {
85 return this.throttleProto
;
88 protected Boolean
getBypassGlobals() {
89 return this.bypassGlobals
;
92 protected QuotaProtos
.SpaceQuota
getSpaceProto() {
93 return this.spaceProto
;
97 * Constructs a new {@link Quotas} message from {@code this}.
99 protected Quotas
toQuotas() {
100 QuotaProtos
.Quotas
.Builder builder
= QuotaProtos
.Quotas
.newBuilder();
101 if (getThrottleProto() != null) {
102 builder
.setThrottle(getThrottleProto());
104 if (getBypassGlobals() != null) {
105 builder
.setBypassGlobals(getBypassGlobals());
107 if (getSpaceProto() != null) {
108 builder
.setSpace(getSpaceProto());
110 return builder
.build();
114 protected GlobalQuotaSettingsImpl
merge(QuotaSettings other
) throws IOException
{
115 // Validate the quota subject
116 validateQuotaTarget(other
);
118 // Propagate the Throttle
119 QuotaProtos
.Throttle
.Builder throttleBuilder
=
120 throttleProto
== null ?
null : throttleProto
.toBuilder();
122 if (other
instanceof ThrottleSettings
) {
123 ThrottleSettings otherThrottle
= (ThrottleSettings
) other
;
124 if (!otherThrottle
.proto
.hasType() || !otherThrottle
.proto
.hasTimedQuota()) {
125 // It means it's a remove request
126 // To prevent the "empty" row in QuotaTableUtil.QUOTA_TABLE_NAME
127 throttleBuilder
= null;
129 QuotaProtos
.ThrottleRequest otherProto
= otherThrottle
.proto
;
130 validateTimedQuota(otherProto
.getTimedQuota());
131 if (throttleBuilder
== null) {
132 throttleBuilder
= QuotaProtos
.Throttle
.newBuilder();
135 switch (otherProto
.getType()) {
137 throttleBuilder
.setReqNum(otherProto
.getTimedQuota());
140 throttleBuilder
.setReqSize(otherProto
.getTimedQuota());
143 throttleBuilder
.setWriteNum(otherProto
.getTimedQuota());
146 throttleBuilder
.setWriteSize(otherProto
.getTimedQuota());
149 throttleBuilder
.setReadNum(otherProto
.getTimedQuota());
152 throttleBuilder
.setReadSize(otherProto
.getTimedQuota());
154 case REQUEST_CAPACITY_UNIT
:
155 throttleBuilder
.setReqCapacityUnit(otherProto
.getTimedQuota());
157 case READ_CAPACITY_UNIT
:
158 throttleBuilder
.setReadCapacityUnit(otherProto
.getTimedQuota());
160 case WRITE_CAPACITY_UNIT
:
161 throttleBuilder
.setWriteCapacityUnit(otherProto
.getTimedQuota());
168 // Propagate the space quota portion
169 QuotaProtos
.SpaceQuota
.Builder spaceBuilder
= (spaceProto
== null
170 ?
null : spaceProto
.toBuilder());
171 if (other
instanceof SpaceLimitSettings
) {
172 if (spaceBuilder
== null) {
173 spaceBuilder
= QuotaProtos
.SpaceQuota
.newBuilder();
175 SpaceLimitSettings settingsToMerge
= (SpaceLimitSettings
) other
;
177 QuotaProtos
.SpaceLimitRequest spaceRequest
= settingsToMerge
.getProto();
179 // The message contained the expect SpaceQuota object
180 if (spaceRequest
.hasQuota()) {
181 SpaceQuota quotaToMerge
= spaceRequest
.getQuota();
182 // Validate that the two settings are for the same target.
183 // SpaceQuotas either apply to a table or a namespace (no user spacequota).
184 if (!Objects
.equals(getTableName(), settingsToMerge
.getTableName())
185 && !Objects
.equals(getNamespace(), settingsToMerge
.getNamespace())) {
186 throw new IllegalArgumentException(
187 "Cannot merge " + settingsToMerge
+ " into " + this);
190 if (quotaToMerge
.getRemove()) {
191 // It means it's a remove request
192 // Update the builder to propagate the removal
193 spaceBuilder
.setRemove(true).clearSoftLimit().clearViolationPolicy();
195 // Add the new settings to the existing settings
196 spaceBuilder
.mergeFrom(quotaToMerge
);
201 boolean removeSpaceBuilder
=
202 (spaceBuilder
== null) || (spaceBuilder
.hasRemove() && spaceBuilder
.getRemove());
204 Boolean bypassGlobals
= this.bypassGlobals
;
205 if (other
instanceof QuotaGlobalsSettingsBypass
) {
206 bypassGlobals
= ((QuotaGlobalsSettingsBypass
) other
).getBypass();
209 if (throttleBuilder
== null && removeSpaceBuilder
&& bypassGlobals
== null) {
213 return new GlobalQuotaSettingsImpl(
214 getUserName(), getTableName(), getNamespace(), getRegionServer(),
215 (throttleBuilder
== null ?
null : throttleBuilder
.build()), bypassGlobals
,
216 (removeSpaceBuilder ?
null : spaceBuilder
.build()));
219 private void validateTimedQuota(final TimedQuota timedQuota
) throws IOException
{
220 if (timedQuota
.getSoftLimit() < 1) {
221 throw new DoNotRetryIOException(new UnsupportedOperationException(
222 "The throttle limit must be greater then 0, got " + timedQuota
.getSoftLimit()));
227 public String
toString() {
228 StringBuilder builder
= new StringBuilder();
229 builder
.append("GlobalQuota: ");
230 if (throttleProto
!= null) {
231 Map
<ThrottleType
,TimedQuota
> throttleQuotas
= buildThrottleQuotas(throttleProto
);
232 builder
.append(" { TYPE => THROTTLE ");
233 for (Entry
<ThrottleType
,TimedQuota
> entry
: throttleQuotas
.entrySet()) {
234 final ThrottleType type
= entry
.getKey();
235 final TimedQuota timedQuota
= entry
.getValue();
236 builder
.append("{THROTTLE_TYPE => ").append(type
.name()).append(", LIMIT => ");
237 if (timedQuota
.hasSoftLimit()) {
242 builder
.append(String
.format("%dreq", timedQuota
.getSoftLimit()));
247 builder
.append(sizeToString(timedQuota
.getSoftLimit()));
249 case REQUEST_CAPACITY_UNIT
:
250 case READ_CAPACITY_UNIT
:
251 case WRITE_CAPACITY_UNIT
:
252 builder
.append(String
.format("%dCU", timedQuota
.getSoftLimit()));
255 } else if (timedQuota
.hasShare()) {
256 builder
.append(String
.format("%.2f%%", timedQuota
.getShare()));
259 builder
.append(timeToString(ProtobufUtil
.toTimeUnit(timedQuota
.getTimeUnit())));
260 if (timedQuota
.hasScope()) {
261 builder
.append(", SCOPE => ");
262 builder
.append(timedQuota
.getScope().toString());
265 builder
.append( "} } ");
267 builder
.append(" {} ");
269 if (bypassGlobals
!= null) {
270 builder
.append(" { GLOBAL_BYPASS => " + bypassGlobals
+ " } ");
272 if (spaceProto
!= null) {
273 builder
.append(" { TYPE => SPACE");
274 if (getTableName() != null) {
275 builder
.append(", TABLE => ").append(getTableName());
277 if (getNamespace() != null) {
278 builder
.append(", NAMESPACE => ").append(getNamespace());
280 if (spaceProto
.getRemove()) {
281 builder
.append(", REMOVE => ").append(spaceProto
.getRemove());
283 builder
.append(", LIMIT => ").append(sizeToString(spaceProto
.getSoftLimit()));
284 builder
.append(", VIOLATION_POLICY => ").append(spaceProto
.getViolationPolicy());
286 builder
.append(" } ");
288 return builder
.toString();
291 private Map
<ThrottleType
,TimedQuota
> buildThrottleQuotas(Throttle proto
) {
292 HashMap
<ThrottleType
,TimedQuota
> quotas
= new HashMap
<>();
293 if (proto
.hasReadNum()) {
294 quotas
.put(ThrottleType
.READ_NUMBER
, proto
.getReadNum());
296 if (proto
.hasReadSize()) {
297 quotas
.put(ThrottleType
.READ_SIZE
, proto
.getReadSize());
299 if (proto
.hasReqNum()) {
300 quotas
.put(ThrottleType
.REQUEST_NUMBER
, proto
.getReqNum());
302 if (proto
.hasReqSize()) {
303 quotas
.put(ThrottleType
.REQUEST_SIZE
, proto
.getReqSize());
305 if (proto
.hasWriteNum()) {
306 quotas
.put(ThrottleType
.WRITE_NUMBER
, proto
.getWriteNum());
308 if (proto
.hasWriteSize()) {
309 quotas
.put(ThrottleType
.WRITE_SIZE
, proto
.getWriteSize());
311 if (proto
.hasReqCapacityUnit()) {
312 quotas
.put(ThrottleType
.REQUEST_CAPACITY_UNIT
, proto
.getReqCapacityUnit());
314 if (proto
.hasReadCapacityUnit()) {
315 quotas
.put(ThrottleType
.READ_CAPACITY_UNIT
, proto
.getReqCapacityUnit());
317 if (proto
.hasWriteCapacityUnit()) {
318 quotas
.put(ThrottleType
.WRITE_CAPACITY_UNIT
, proto
.getWriteCapacityUnit());