You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hbase.apache.org by mb...@apache.org on 2014/09/18 23:45:24 UTC
[7/7] git commit: HBASE-11598 Add simple rpc throttling
HBASE-11598 Add simple rpc throttling
Project: http://git-wip-us.apache.org/repos/asf/hbase/repo
Commit: http://git-wip-us.apache.org/repos/asf/hbase/commit/bd8df9cc
Tree: http://git-wip-us.apache.org/repos/asf/hbase/tree/bd8df9cc
Diff: http://git-wip-us.apache.org/repos/asf/hbase/diff/bd8df9cc
Branch: refs/heads/master
Commit: bd8df9ccd4de11542d3ce9ecd59566d88e2bb277
Parents: f11b512
Author: Matteo Bertozzi <ma...@cloudera.com>
Authored: Thu Sep 18 01:51:15 2014 +0100
Committer: Matteo Bertozzi <ma...@cloudera.com>
Committed: Thu Sep 18 22:38:30 2014 +0100
----------------------------------------------------------------------
dev-support/test-patch.properties | 2 +-
.../java/org/apache/hadoop/hbase/Chore.java | 7 +
.../org/apache/hadoop/hbase/client/Admin.java | 20 +
.../hadoop/hbase/client/ConnectionManager.java | 11 +-
.../apache/hadoop/hbase/client/HBaseAdmin.java | 31 +
.../hadoop/hbase/protobuf/ProtobufUtil.java | 157 +
.../quotas/InvalidQuotaSettingsException.java | 30 +
.../hbase/quotas/QuotaExceededException.java | 30 +
.../apache/hadoop/hbase/quotas/QuotaFilter.java | 110 +
.../hadoop/hbase/quotas/QuotaRetriever.java | 166 +
.../apache/hadoop/hbase/quotas/QuotaScope.java | 43 +
.../hadoop/hbase/quotas/QuotaSettings.java | 125 +
.../hbase/quotas/QuotaSettingsFactory.java | 267 ++
.../hadoop/hbase/quotas/QuotaTableUtil.java | 418 ++
.../apache/hadoop/hbase/quotas/QuotaType.java | 31 +
.../hadoop/hbase/quotas/ThrottleSettings.java | 109 +
.../hadoop/hbase/quotas/ThrottleType.java | 34 +
.../hbase/quotas/ThrottlingException.java | 166 +
.../org/apache/hadoop/hbase/util/Sleeper.java | 7 +
.../org/apache/hadoop/hbase/util/Bytes.java | 23 +-
hbase-protocol/pom.xml | 1 +
.../hbase/protobuf/generated/HBaseProtos.java | 136 +-
.../hbase/protobuf/generated/MasterProtos.java | 2278 ++++++++-
.../hbase/protobuf/generated/QuotaProtos.java | 4378 ++++++++++++++++++
hbase-protocol/src/main/protobuf/HBase.proto | 10 +
hbase-protocol/src/main/protobuf/Master.proto | 22 +-
hbase-protocol/src/main/protobuf/Quota.proto | 73 +
.../BaseMasterAndRegionObserver.java | 51 +
.../hbase/coprocessor/BaseMasterObserver.java | 52 +-
.../hbase/coprocessor/MasterObserver.java | 105 +
.../org/apache/hadoop/hbase/ipc/RpcServer.java | 5 +
.../hadoop/hbase/ipc/RpcServerInterface.java | 2 +
.../org/apache/hadoop/hbase/master/HMaster.java | 17 +
.../hbase/master/MasterCoprocessorHost.java | 105 +
.../hadoop/hbase/master/MasterRpcServices.java | 13 +
.../hadoop/hbase/master/MasterServices.java | 6 +
.../hbase/quotas/DefaultOperationQuota.java | 146 +
.../hadoop/hbase/quotas/MasterQuotaManager.java | 440 ++
.../hadoop/hbase/quotas/NoopOperationQuota.java | 86 +
.../hadoop/hbase/quotas/NoopQuotaLimiter.java | 90 +
.../hadoop/hbase/quotas/OperationQuota.java | 130 +
.../apache/hadoop/hbase/quotas/QuotaCache.java | 328 ++
.../hadoop/hbase/quotas/QuotaLimiter.java | 80 +
.../hbase/quotas/QuotaLimiterFactory.java | 42 +
.../apache/hadoop/hbase/quotas/QuotaState.java | 122 +
.../apache/hadoop/hbase/quotas/QuotaUtil.java | 319 ++
.../apache/hadoop/hbase/quotas/RateLimiter.java | 181 +
.../hbase/quotas/RegionServerQuotaManager.java | 200 +
.../hadoop/hbase/quotas/TimeBasedLimiter.java | 207 +
.../hadoop/hbase/quotas/UserQuotaState.java | 209 +
.../hbase/regionserver/HRegionServer.java | 35 +
.../hbase/regionserver/RSRpcServices.java | 81 +-
.../regionserver/RegionServerServices.java | 13 +
.../hbase/security/access/AccessController.java | 31 +
.../util/BoundedPriorityBlockingQueue.java | 1 +
.../hadoop/hbase/MockRegionServerServices.java | 14 +
.../hbase/coprocessor/TestMasterObserver.java | 55 +-
.../hadoop/hbase/master/MockRegionServer.java | 13 +
.../hadoop/hbase/master/TestCatalogJanitor.java | 6 +
.../hadoop/hbase/quotas/TestQuotaAdmin.java | 218 +
.../hadoop/hbase/quotas/TestQuotaState.java | 236 +
.../hadoop/hbase/quotas/TestQuotaTableUtil.java | 171 +
.../hadoop/hbase/quotas/TestQuotaThrottle.java | 423 ++
.../hadoop/hbase/quotas/TestRateLimiter.java | 115 +
.../security/access/TestAccessController.java | 64 +-
hbase-shell/src/main/ruby/hbase.rb | 9 +
hbase-shell/src/main/ruby/hbase/hbase.rb | 5 +
hbase-shell/src/main/ruby/hbase/quotas.rb | 213 +
hbase-shell/src/main/ruby/shell.rb | 13 +
hbase-shell/src/main/ruby/shell/commands.rb | 8 +-
.../src/main/ruby/shell/commands/list_quotas.rb | 52 +
.../src/main/ruby/shell/commands/set_quota.rb | 70 +
72 files changed, 13242 insertions(+), 225 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/hbase/blob/bd8df9cc/dev-support/test-patch.properties
----------------------------------------------------------------------
diff --git a/dev-support/test-patch.properties b/dev-support/test-patch.properties
index e9edecb..4ecad34 100644
--- a/dev-support/test-patch.properties
+++ b/dev-support/test-patch.properties
@@ -19,7 +19,7 @@ MAVEN_OPTS="-Xmx3100M"
# Please update the per-module test-patch.properties if you update this file.
OK_RELEASEAUDIT_WARNINGS=0
-OK_FINDBUGS_WARNINGS=89
+OK_FINDBUGS_WARNINGS=95
# Allow two warnings. Javadoc complains about sun.misc.Unsafe use. See HBASE-7457
OK_JAVADOC_WARNINGS=2
http://git-wip-us.apache.org/repos/asf/hbase/blob/bd8df9cc/hbase-client/src/main/java/org/apache/hadoop/hbase/Chore.java
----------------------------------------------------------------------
diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/Chore.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/Chore.java
index e870d12..0addc71 100644
--- a/hbase-client/src/main/java/org/apache/hadoop/hbase/Chore.java
+++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/Chore.java
@@ -65,6 +65,13 @@ public abstract class Chore extends HasThread {
}
/**
+ * @return the sleep period in milliseconds
+ */
+ public final int getPeriod() {
+ return sleeper.getPeriod();
+ }
+
+ /**
* @see java.lang.Thread#run()
*/
@Override
http://git-wip-us.apache.org/repos/asf/hbase/blob/bd8df9cc/hbase-client/src/main/java/org/apache/hadoop/hbase/client/Admin.java
----------------------------------------------------------------------
diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/Admin.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/Admin.java
index 8ea8f41..722b5c2 100644
--- a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/Admin.java
+++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/Admin.java
@@ -38,6 +38,9 @@ import org.apache.hadoop.hbase.ipc.CoprocessorRpcChannel;
import org.apache.hadoop.hbase.protobuf.generated.AdminProtos;
import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos;
import org.apache.hadoop.hbase.protobuf.generated.MasterProtos;
+import org.apache.hadoop.hbase.quotas.QuotaFilter;
+import org.apache.hadoop.hbase.quotas.QuotaRetriever;
+import org.apache.hadoop.hbase.quotas.QuotaSettings;
import org.apache.hadoop.hbase.regionserver.wal.FailedLogCloseException;
import org.apache.hadoop.hbase.snapshot.HBaseSnapshotException;
import org.apache.hadoop.hbase.snapshot.RestoreSnapshotException;
@@ -1207,6 +1210,23 @@ public interface Admin extends Abortable, Closeable {
void deleteSnapshots(final Pattern pattern) throws IOException;
/**
+ * Apply the new quota settings.
+ *
+ * @param quota the quota settings
+ * @throws IOException if a remote or network exception occurs
+ */
+ void setQuota(final QuotaSettings quota) throws IOException;
+
+ /**
+ * Return a QuotaRetriever to list the quotas based on the filter.
+ *
+ * @param filter the quota settings filter
+ * @return the quota retriever
+ * @throws IOException if a remote or network exception occurs
+ */
+ QuotaRetriever getQuotaRetriever(final QuotaFilter filter) throws IOException;
+
+ /**
* Creates and returns a {@link com.google.protobuf.RpcChannel} instance connected to the active
* master. <p> The obtained {@link com.google.protobuf.RpcChannel} instance can be used to access
* a published coprocessor {@link com.google.protobuf.Service} using standard protobuf service
http://git-wip-us.apache.org/repos/asf/hbase/blob/bd8df9cc/hbase-client/src/main/java/org/apache/hadoop/hbase/client/ConnectionManager.java
----------------------------------------------------------------------
diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/ConnectionManager.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/ConnectionManager.java
index bbf180e..e16b52f 100644
--- a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/ConnectionManager.java
+++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/ConnectionManager.java
@@ -148,13 +148,15 @@ import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.RunCatalogScanReq
import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.RunCatalogScanResponse;
import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.SetBalancerRunningRequest;
import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.SetBalancerRunningResponse;
+import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.SetQuotaRequest;
+import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.SetQuotaResponse;
import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.ShutdownRequest;
import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.ShutdownResponse;
import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.SnapshotRequest;
import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.SnapshotResponse;
import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.StopMasterRequest;
-import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.TruncateTableRequest;
import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.StopMasterResponse;
+import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.TruncateTableRequest;
import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.TruncateTableResponse;
import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.UnassignRegionRequest;
import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.UnassignRegionResponse;
@@ -2033,6 +2035,13 @@ class ConnectionManager {
throws ServiceException {
return stub.getClusterStatus(controller, request);
}
+
+ @Override
+ public SetQuotaResponse setQuota(
+ RpcController controller, SetQuotaRequest request)
+ throws ServiceException {
+ return stub.setQuota(controller, request);
+ }
};
}
http://git-wip-us.apache.org/repos/asf/hbase/blob/bd8df9cc/hbase-client/src/main/java/org/apache/hadoop/hbase/client/HBaseAdmin.java
----------------------------------------------------------------------
diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/HBaseAdmin.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/HBaseAdmin.java
index b659e87..aa8d21e 100644
--- a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/HBaseAdmin.java
+++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/HBaseAdmin.java
@@ -126,12 +126,16 @@ import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.MoveRegionRequest
import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.RestoreSnapshotRequest;
import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.RestoreSnapshotResponse;
import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.SetBalancerRunningRequest;
+import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.SetQuotaRequest;
import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.ShutdownRequest;
import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.SnapshotRequest;
import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.SnapshotResponse;
import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.StopMasterRequest;
import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.TruncateTableRequest;
import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.UnassignRegionRequest;
+import org.apache.hadoop.hbase.quotas.QuotaFilter;
+import org.apache.hadoop.hbase.quotas.QuotaRetriever;
+import org.apache.hadoop.hbase.quotas.QuotaSettings;
import org.apache.hadoop.hbase.regionserver.wal.FailedLogCloseException;
import org.apache.hadoop.hbase.snapshot.ClientSnapshotDescriptionUtils;
import org.apache.hadoop.hbase.snapshot.HBaseSnapshotException;
@@ -3552,6 +3556,33 @@ public class HBaseAdmin implements Admin {
}
/**
+ * Apply the new quota settings.
+ *
+ * @param quota the quota settings
+ * @throws IOException if a remote or network exception occurs
+ */
+ public void setQuota(final QuotaSettings quota) throws IOException {
+ executeCallable(new MasterCallable<Void>(getConnection()) {
+ @Override
+ public Void call(int callTimeout) throws ServiceException {
+ this.master.setQuota(null, QuotaSettings.buildSetQuotaRequestProto(quota));
+ return null;
+ }
+ });
+ }
+
+ /**
+ * Return a Quota Scanner to list the quotas based on the filter.
+ *
+ * @param filter the quota settings filter
+ * @return the quota scanner
+ * @throws IOException if a remote or network exception occurs
+ */
+ public QuotaRetriever getQuotaRetriever(final QuotaFilter filter) throws IOException {
+ return QuotaRetriever.open(conf, filter);
+ }
+
+ /**
* Parent of {@link MasterCallable} and {@link MasterCallable}.
* Has common methods.
* @param <V>
http://git-wip-us.apache.org/repos/asf/hbase/blob/bd8df9cc/hbase-client/src/main/java/org/apache/hadoop/hbase/protobuf/ProtobufUtil.java
----------------------------------------------------------------------
diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/protobuf/ProtobufUtil.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/protobuf/ProtobufUtil.java
index f0d8e86..dff4388 100644
--- a/hbase-client/src/main/java/org/apache/hadoop/hbase/protobuf/ProtobufUtil.java
+++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/protobuf/ProtobufUtil.java
@@ -29,6 +29,22 @@ import com.google.protobuf.RpcChannel;
import com.google.protobuf.Service;
import com.google.protobuf.ServiceException;
import com.google.protobuf.TextFormat;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.NavigableSet;
+import java.util.concurrent.TimeUnit;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.hbase.Cell;
@@ -108,6 +124,7 @@ import org.apache.hadoop.hbase.protobuf.generated.MapReduceProtos;
import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.CreateTableRequest;
import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.GetTableDescriptorsResponse;
import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.MasterService;
+import org.apache.hadoop.hbase.protobuf.generated.QuotaProtos;
import org.apache.hadoop.hbase.protobuf.generated.RegionServerStatusProtos.RegionServerReportRequest;
import org.apache.hadoop.hbase.protobuf.generated.RegionServerStatusProtos.RegionServerStartupRequest;
import org.apache.hadoop.hbase.protobuf.generated.WALProtos.CompactionDescriptor;
@@ -115,6 +132,9 @@ import org.apache.hadoop.hbase.protobuf.generated.WALProtos.FlushDescriptor;
import org.apache.hadoop.hbase.protobuf.generated.WALProtos.FlushDescriptor.FlushAction;
import org.apache.hadoop.hbase.protobuf.generated.WALProtos.RegionEventDescriptor;
import org.apache.hadoop.hbase.protobuf.generated.WALProtos.RegionEventDescriptor.EventType;
+import org.apache.hadoop.hbase.quotas.QuotaType;
+import org.apache.hadoop.hbase.quotas.QuotaScope;
+import org.apache.hadoop.hbase.quotas.ThrottleType;
import org.apache.hadoop.hbase.security.access.Permission;
import org.apache.hadoop.hbase.security.access.TablePermission;
import org.apache.hadoop.hbase.security.access.UserPermission;
@@ -2794,4 +2814,141 @@ public final class ProtobufUtil {
}
return result;
}
+
+ /**
+ * Convert a protocol buffer TimeUnit to a client TimeUnit
+ *
+ * @param proto
+ * @return the converted client TimeUnit
+ */
+ public static TimeUnit toTimeUnit(final HBaseProtos.TimeUnit proto) {
+ switch (proto) {
+ case NANOSECONDS: return TimeUnit.NANOSECONDS;
+ case MICROSECONDS: return TimeUnit.MICROSECONDS;
+ case MILLISECONDS: return TimeUnit.MILLISECONDS;
+ case SECONDS: return TimeUnit.SECONDS;
+ case MINUTES: return TimeUnit.MINUTES;
+ case HOURS: return TimeUnit.HOURS;
+ case DAYS: return TimeUnit.DAYS;
+ }
+ throw new RuntimeException("Invalid TimeUnit " + proto);
+ }
+
+ /**
+ * Convert a client TimeUnit to a protocol buffer TimeUnit
+ *
+ * @param timeUnit
+ * @return the converted protocol buffer TimeUnit
+ */
+ public static HBaseProtos.TimeUnit toProtoTimeUnit(final TimeUnit timeUnit) {
+ switch (timeUnit) {
+ case NANOSECONDS: return HBaseProtos.TimeUnit.NANOSECONDS;
+ case MICROSECONDS: return HBaseProtos.TimeUnit.MICROSECONDS;
+ case MILLISECONDS: return HBaseProtos.TimeUnit.MILLISECONDS;
+ case SECONDS: return HBaseProtos.TimeUnit.SECONDS;
+ case MINUTES: return HBaseProtos.TimeUnit.MINUTES;
+ case HOURS: return HBaseProtos.TimeUnit.HOURS;
+ case DAYS: return HBaseProtos.TimeUnit.DAYS;
+ }
+ throw new RuntimeException("Invalid TimeUnit " + timeUnit);
+ }
+
+ /**
+ * Convert a protocol buffer ThrottleType to a client ThrottleType
+ *
+ * @param proto
+ * @return the converted client ThrottleType
+ */
+ public static ThrottleType toThrottleType(final QuotaProtos.ThrottleType proto) {
+ switch (proto) {
+ case REQUEST_NUMBER: return ThrottleType.REQUEST_NUMBER;
+ case REQUEST_SIZE: return ThrottleType.REQUEST_SIZE;
+ }
+ throw new RuntimeException("Invalid ThrottleType " + proto);
+ }
+
+ /**
+ * Convert a client ThrottleType to a protocol buffer ThrottleType
+ *
+ * @param type
+ * @return the converted protocol buffer ThrottleType
+ */
+ public static QuotaProtos.ThrottleType toProtoThrottleType(final ThrottleType type) {
+ switch (type) {
+ case REQUEST_NUMBER: return QuotaProtos.ThrottleType.REQUEST_NUMBER;
+ case REQUEST_SIZE: return QuotaProtos.ThrottleType.REQUEST_SIZE;
+ }
+ throw new RuntimeException("Invalid ThrottleType " + type);
+ }
+
+ /**
+ * Convert a protocol buffer QuotaScope to a client QuotaScope
+ *
+ * @param proto
+ * @return the converted client QuotaScope
+ */
+ public static QuotaScope toQuotaScope(final QuotaProtos.QuotaScope proto) {
+ switch (proto) {
+ case CLUSTER: return QuotaScope.CLUSTER;
+ case MACHINE: return QuotaScope.MACHINE;
+ }
+ throw new RuntimeException("Invalid QuotaScope " + proto);
+ }
+
+ /**
+ * Convert a client QuotaScope to a protocol buffer QuotaScope
+ *
+ * @param scope
+ * @return the converted protocol buffer QuotaScope
+ */
+ public static QuotaProtos.QuotaScope toProtoQuotaScope(final QuotaScope scope) {
+ switch (scope) {
+ case CLUSTER: return QuotaProtos.QuotaScope.CLUSTER;
+ case MACHINE: return QuotaProtos.QuotaScope.MACHINE;
+ }
+ throw new RuntimeException("Invalid QuotaScope " + scope);
+ }
+
+ /**
+ * Convert a protocol buffer QuotaType to a client QuotaType
+ *
+ * @param proto
+ * @return the converted client QuotaType
+ */
+ public static QuotaType toQuotaScope(final QuotaProtos.QuotaType proto) {
+ switch (proto) {
+ case THROTTLE: return QuotaType.THROTTLE;
+ }
+ throw new RuntimeException("Invalid QuotaType " + proto);
+ }
+
+ /**
+ * Convert a client QuotaType to a protocol buffer QuotaType
+ *
+ * @param type
+ * @return the converted protocol buffer QuotaType
+ */
+ public static QuotaProtos.QuotaType toProtoQuotaScope(final QuotaType type) {
+ switch (type) {
+ case THROTTLE: return QuotaProtos.QuotaType.THROTTLE;
+ }
+ throw new RuntimeException("Invalid QuotaType " + type);
+ }
+
+ /**
+ * Build a protocol buffer TimedQuota
+ *
+ * @param limit the allowed number of request/data per timeUnit
+ * @param timeUnit the limit time unit
+ * @param scope the quota scope
+ * @return the protocol buffer TimedQuota
+ */
+ public static QuotaProtos.TimedQuota toTimedQuota(final long limit, final TimeUnit timeUnit,
+ final QuotaScope scope) {
+ return QuotaProtos.TimedQuota.newBuilder()
+ .setSoftLimit(limit)
+ .setTimeUnit(toProtoTimeUnit(timeUnit))
+ .setScope(toProtoQuotaScope(scope))
+ .build();
+ }
}
http://git-wip-us.apache.org/repos/asf/hbase/blob/bd8df9cc/hbase-client/src/main/java/org/apache/hadoop/hbase/quotas/InvalidQuotaSettingsException.java
----------------------------------------------------------------------
diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/quotas/InvalidQuotaSettingsException.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/quotas/InvalidQuotaSettingsException.java
new file mode 100644
index 0000000..2675e3a
--- /dev/null
+++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/quotas/InvalidQuotaSettingsException.java
@@ -0,0 +1,30 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hadoop.hbase.quotas;
+
+import org.apache.hadoop.hbase.DoNotRetryIOException;
+
+/**
+ * Generic quota exceeded exception for invalid settings
+ */
+public class InvalidQuotaSettingsException extends DoNotRetryIOException {
+ public InvalidQuotaSettingsException(String msg) {
+ super(msg);
+ }
+}
http://git-wip-us.apache.org/repos/asf/hbase/blob/bd8df9cc/hbase-client/src/main/java/org/apache/hadoop/hbase/quotas/QuotaExceededException.java
----------------------------------------------------------------------
diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/quotas/QuotaExceededException.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/quotas/QuotaExceededException.java
new file mode 100644
index 0000000..d9bea8c
--- /dev/null
+++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/quotas/QuotaExceededException.java
@@ -0,0 +1,30 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hadoop.hbase.quotas;
+
+import org.apache.hadoop.hbase.DoNotRetryIOException;
+
+/**
+ * Generic quota exceeded exception
+ */
+public class QuotaExceededException extends DoNotRetryIOException {
+ public QuotaExceededException(String msg) {
+ super(msg);
+ }
+}
http://git-wip-us.apache.org/repos/asf/hbase/blob/bd8df9cc/hbase-client/src/main/java/org/apache/hadoop/hbase/quotas/QuotaFilter.java
----------------------------------------------------------------------
diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/quotas/QuotaFilter.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/quotas/QuotaFilter.java
new file mode 100644
index 0000000..0408bd8
--- /dev/null
+++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/quotas/QuotaFilter.java
@@ -0,0 +1,110 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.hadoop.hbase.quotas;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.apache.hadoop.classification.InterfaceAudience;
+import org.apache.hadoop.classification.InterfaceStability;
+import org.apache.hadoop.hbase.util.Strings;
+
+/**
+ * Filter to use to filter the QuotaRetriever results.
+ */
+@InterfaceAudience.Public
+@InterfaceStability.Evolving
+public class QuotaFilter {
+ private Set<QuotaType> types = new HashSet<QuotaType>();
+ private boolean hasFilters = false;
+ private String namespaceRegex;
+ private String tableRegex;
+ private String userRegex;
+
+ public QuotaFilter() {
+ }
+
+ /**
+ * Set the user filter regex
+ * @param regex the user filter
+ * @return the quota filter object
+ */
+ public QuotaFilter setUserFilter(final String regex) {
+ this.userRegex = regex;
+ hasFilters |= !Strings.isEmpty(regex);
+ return this;
+ }
+
+ /**
+ * Set the table filter regex
+ * @param regex the table filter
+ * @return the quota filter object
+ */
+ public QuotaFilter setTableFilter(final String regex) {
+ this.tableRegex = regex;
+ hasFilters |= !Strings.isEmpty(regex);
+ return this;
+ }
+
+ /**
+ * Set the namespace filter regex
+ * @param regex the namespace filter
+ * @return the quota filter object
+ */
+ public QuotaFilter setNamespaceFilter(final String regex) {
+ this.namespaceRegex = regex;
+ hasFilters |= !Strings.isEmpty(regex);
+ return this;
+ }
+
+ /**
+ * Add a type to the filter list
+ * @param type the type to filter on
+ * @return the quota filter object
+ */
+ public QuotaFilter addTypeFilter(final QuotaType type) {
+ this.types.add(type);
+ hasFilters |= true;
+ return this;
+ }
+
+ /** @return true if the filter is empty */
+ public boolean isNull() {
+ return !hasFilters;
+ }
+
+ /** @return the QuotaType types that we want to filter one */
+ public Set<QuotaType> getTypeFilters() {
+ return types;
+ }
+
+ /** @return the Namespace filter regex */
+ public String getNamespaceFilter() {
+ return namespaceRegex;
+ }
+
+ /** @return the Table filter regex */
+ public String getTableFilter() {
+ return tableRegex;
+ }
+
+ /** @return the User filter regex */
+ public String getUserFilter() {
+ return userRegex;
+ }
+}
http://git-wip-us.apache.org/repos/asf/hbase/blob/bd8df9cc/hbase-client/src/main/java/org/apache/hadoop/hbase/quotas/QuotaRetriever.java
----------------------------------------------------------------------
diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/quotas/QuotaRetriever.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/quotas/QuotaRetriever.java
new file mode 100644
index 0000000..85b1daf
--- /dev/null
+++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/quotas/QuotaRetriever.java
@@ -0,0 +1,166 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hadoop.hbase.quotas;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.Queue;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.hadoop.classification.InterfaceAudience;
+import org.apache.hadoop.classification.InterfaceStability;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.hbase.TableName;
+import org.apache.hadoop.hbase.client.Result;
+import org.apache.hadoop.hbase.client.ResultScanner;
+import org.apache.hadoop.hbase.client.Scan;
+import org.apache.hadoop.hbase.client.HTable;
+import org.apache.hadoop.hbase.protobuf.generated.QuotaProtos.Quotas;
+import org.apache.hadoop.util.StringUtils;
+
+/**
+ * Scanner to iterate over the quota settings.
+ */
+@InterfaceAudience.Public
+@InterfaceStability.Evolving
+public class QuotaRetriever implements Closeable, Iterable<QuotaSettings> {
+ private static final Log LOG = LogFactory.getLog(QuotaRetriever.class);
+
+ private final Queue<QuotaSettings> cache = new LinkedList<QuotaSettings>();
+ private ResultScanner scanner;
+ private HTable table;
+
+ private QuotaRetriever() {
+ }
+
+ void init(final Configuration conf, final Scan scan) throws IOException {
+ table = new HTable(conf, QuotaTableUtil.QUOTA_TABLE_NAME);
+ try {
+ scanner = table.getScanner(scan);
+ } catch (IOException e) {
+ table.close();
+ throw e;
+ }
+ }
+
+ public void close() throws IOException {
+ table.close();
+ }
+
+ public QuotaSettings next() throws IOException {
+ if (cache.isEmpty()) {
+ Result result = scanner.next();
+ if (result == null) return null;
+
+ QuotaTableUtil.parseResult(result, new QuotaTableUtil.QuotasVisitor() {
+ @Override
+ public void visitUserQuotas(String userName, Quotas quotas) {
+ cache.addAll(QuotaSettingsFactory.fromUserQuotas(userName, quotas));
+ }
+
+ @Override
+ public void visitUserQuotas(String userName, TableName table, Quotas quotas) {
+ cache.addAll(QuotaSettingsFactory.fromUserQuotas(userName, table, quotas));
+ }
+
+ @Override
+ public void visitUserQuotas(String userName, String namespace, Quotas quotas) {
+ cache.addAll(QuotaSettingsFactory.fromUserQuotas(userName, namespace, quotas));
+ }
+
+ @Override
+ public void visitTableQuotas(TableName tableName, Quotas quotas) {
+ cache.addAll(QuotaSettingsFactory.fromTableQuotas(tableName, quotas));
+ }
+
+ @Override
+ public void visitNamespaceQuotas(String namespace, Quotas quotas) {
+ cache.addAll(QuotaSettingsFactory.fromNamespaceQuotas(namespace, quotas));
+ }
+ });
+ }
+ return cache.poll();
+ }
+
+ @Override
+ public Iterator<QuotaSettings> iterator() {
+ return new Iter();
+ }
+
+ private class Iter implements Iterator<QuotaSettings> {
+ QuotaSettings cache;
+
+ public Iter() {
+ try {
+ cache = QuotaRetriever.this.next();
+ } catch (IOException e) {
+ LOG.warn(StringUtils.stringifyException(e));
+ }
+ }
+
+ @Override
+ public boolean hasNext() {
+ return cache != null;
+ }
+
+ @Override
+ public QuotaSettings next() {
+ QuotaSettings result = cache;
+ try {
+ cache = QuotaRetriever.this.next();
+ } catch (IOException e) {
+ LOG.warn(StringUtils.stringifyException(e));
+ }
+ return result;
+ }
+
+ @Override
+ public void remove() {
+ throw new RuntimeException("remove() not supported");
+ }
+ }
+
+ /**
+ * Open a QuotaRetriever with no filter, all the quota settings will be returned.
+ * @param conf Configuration object to use.
+ * @return the QuotaRetriever
+ * @throws IOException if a remote or network exception occurs
+ */
+ public static QuotaRetriever open(final Configuration conf) throws IOException {
+ return open(conf, null);
+ }
+
+ /**
+ * Open a QuotaRetriever with the specified filter.
+ * @param conf Configuration object to use.
+ * @param filter the QuotaFilter
+ * @return the QuotaRetriever
+ * @throws IOException if a remote or network exception occurs
+ */
+ public static QuotaRetriever open(final Configuration conf, final QuotaFilter filter)
+ throws IOException {
+ Scan scan = QuotaTableUtil.makeScan(filter);
+ QuotaRetriever scanner = new QuotaRetriever();
+ scanner.init(conf, scan);
+ return scanner;
+ }
+}
http://git-wip-us.apache.org/repos/asf/hbase/blob/bd8df9cc/hbase-client/src/main/java/org/apache/hadoop/hbase/quotas/QuotaScope.java
----------------------------------------------------------------------
diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/quotas/QuotaScope.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/quotas/QuotaScope.java
new file mode 100644
index 0000000..0dc6347
--- /dev/null
+++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/quotas/QuotaScope.java
@@ -0,0 +1,43 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.hadoop.hbase.quotas;
+
+import org.apache.hadoop.classification.InterfaceAudience;
+import org.apache.hadoop.classification.InterfaceStability;
+
+/**
+ * Describe the Scope of the quota rules.
+ * The quota can be enforced at the cluster level or at machine level.
+ */
+@InterfaceAudience.Public
+@InterfaceStability.Evolving
+public enum QuotaScope {
+ /**
+ * The specified throttling rules will be applied at the cluster level.
+ * A limit of 100req/min means 100req/min in total.
+ * If you execute 50req on a machine and then 50req on another machine
+ * then you have to wait your quota to fill up.
+ */
+ CLUSTER,
+
+ /**
+ * The specified throttling rules will be applied on the machine level.
+ * A limit of 100req/min means that each machine can execute 100req/min.
+ */
+ MACHINE,
+}
http://git-wip-us.apache.org/repos/asf/hbase/blob/bd8df9cc/hbase-client/src/main/java/org/apache/hadoop/hbase/quotas/QuotaSettings.java
----------------------------------------------------------------------
diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/quotas/QuotaSettings.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/quotas/QuotaSettings.java
new file mode 100644
index 0000000..623e8d5
--- /dev/null
+++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/quotas/QuotaSettings.java
@@ -0,0 +1,125 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.hadoop.hbase.quotas;
+
+import java.util.concurrent.TimeUnit;
+
+import org.apache.hadoop.classification.InterfaceAudience;
+import org.apache.hadoop.classification.InterfaceStability;
+import org.apache.hadoop.hbase.TableName;
+import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
+import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.SetQuotaRequest;
+import org.apache.hadoop.hbase.quotas.ThrottleType;
+
+@InterfaceAudience.Public
+@InterfaceStability.Evolving
+public abstract class QuotaSettings {
+ private final String userName;
+ private final String namespace;
+ private final TableName tableName;
+
+ protected QuotaSettings(final String userName, final TableName tableName,
+ final String namespace) {
+ this.userName = userName;
+ this.namespace = namespace;
+ this.tableName = tableName;
+ }
+
+ public abstract QuotaType getQuotaType();
+
+ public String getUserName() {
+ return userName;
+ }
+
+ public TableName getTableName() {
+ return tableName;
+ }
+
+ public String getNamespace() {
+ return namespace;
+ }
+
+ /**
+ * Convert a QuotaSettings to a protocol buffer SetQuotaRequest.
+ * This is used internally by the Admin client to serialize the quota settings
+ * and send them to the master.
+ */
+ public static SetQuotaRequest buildSetQuotaRequestProto(final QuotaSettings settings) {
+ SetQuotaRequest.Builder builder = SetQuotaRequest.newBuilder();
+ if (settings.getUserName() != null) {
+ builder.setUserName(settings.getUserName());
+ }
+ if (settings.getTableName() != null) {
+ builder.setTableName(ProtobufUtil.toProtoTableName(settings.getTableName()));
+ }
+ if (settings.getNamespace() != null) {
+ builder.setNamespace(settings.getNamespace());
+ }
+ settings.setupSetQuotaRequest(builder);
+ return builder.build();
+ }
+
+ /**
+ * Called by toSetQuotaRequestProto()
+ * the subclass should implement this method to set the specific SetQuotaRequest
+ * properties.
+ */
+ protected abstract void setupSetQuotaRequest(SetQuotaRequest.Builder builder);
+
+ protected String ownerToString() {
+ StringBuilder builder = new StringBuilder();
+ if (userName != null) {
+ builder.append("USER => '");
+ builder.append(userName);
+ builder.append("', ");
+ }
+ if (tableName != null) {
+ builder.append("TABLE => '");
+ builder.append(tableName.toString());
+ builder.append("', ");
+ }
+ if (namespace != null) {
+ builder.append("NAMESPACE => '");
+ builder.append(namespace);
+ builder.append("', ");
+ }
+ return builder.toString();
+ }
+
+ protected static String sizeToString(final long size) {
+ if (size >= (1L << 50)) return String.format("%dP", size / (1L << 50));
+ if (size >= (1L << 40)) return String.format("%dT", size / (1L << 40));
+ if (size >= (1L << 30)) return String.format("%dG", size / (1L << 30));
+ if (size >= (1L << 20)) return String.format("%dM", size / (1L << 20));
+ if (size >= (1L << 10)) return String.format("%dK", size / (1L << 10));
+ return String.format("%dB", size);
+ }
+
+ protected static String timeToString(final TimeUnit timeUnit) {
+ switch (timeUnit) {
+ case NANOSECONDS: return "nsec";
+ case MICROSECONDS: return "usec";
+ case MILLISECONDS: return "msec";
+ case SECONDS: return "sec";
+ case MINUTES: return "min";
+ case HOURS: return "hour";
+ case DAYS: return "day";
+ }
+ throw new RuntimeException("Invalid TimeUnit " + timeUnit);
+ }
+}
http://git-wip-us.apache.org/repos/asf/hbase/blob/bd8df9cc/hbase-client/src/main/java/org/apache/hadoop/hbase/quotas/QuotaSettingsFactory.java
----------------------------------------------------------------------
diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/quotas/QuotaSettingsFactory.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/quotas/QuotaSettingsFactory.java
new file mode 100644
index 0000000..2050efe
--- /dev/null
+++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/quotas/QuotaSettingsFactory.java
@@ -0,0 +1,267 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.hadoop.hbase.quotas;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.hadoop.classification.InterfaceAudience;
+import org.apache.hadoop.classification.InterfaceStability;
+import org.apache.hadoop.hbase.TableName;
+import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
+import org.apache.hadoop.hbase.protobuf.generated.QuotaProtos;
+import org.apache.hadoop.hbase.protobuf.generated.QuotaProtos.Quotas;
+import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.SetQuotaRequest;
+
+@InterfaceAudience.Public
+@InterfaceStability.Evolving
+public class QuotaSettingsFactory {
+ static class QuotaGlobalsSettingsBypass extends QuotaSettings {
+ private final boolean bypassGlobals;
+
+ QuotaGlobalsSettingsBypass(final String userName, final TableName tableName,
+ final String namespace, final boolean bypassGlobals) {
+ super(userName, tableName, namespace);
+ this.bypassGlobals = bypassGlobals;
+ }
+
+ @Override
+ public QuotaType getQuotaType() {
+ return QuotaType.GLOBAL_BYPASS;
+ }
+
+ @Override
+ protected void setupSetQuotaRequest(SetQuotaRequest.Builder builder) {
+ builder.setBypassGlobals(bypassGlobals);
+ }
+
+ @Override
+ public String toString() {
+ return "GLOBAL_BYPASS => " + bypassGlobals;
+ }
+ }
+
+ /* ==========================================================================
+ * QuotaSettings from the Quotas object
+ */
+ static List<QuotaSettings> fromUserQuotas(final String userName, final Quotas quotas) {
+ return fromQuotas(userName, null, null, quotas);
+ }
+
+ static List<QuotaSettings> fromUserQuotas(final String userName, final TableName tableName,
+ final Quotas quotas) {
+ return fromQuotas(userName, tableName, null, quotas);
+ }
+
+ static List<QuotaSettings> fromUserQuotas(final String userName, final String namespace,
+ final Quotas quotas) {
+ return fromQuotas(userName, null, namespace, quotas);
+ }
+
+ static List<QuotaSettings> fromTableQuotas(final TableName tableName, final Quotas quotas) {
+ return fromQuotas(null, tableName, null, quotas);
+ }
+
+ static List<QuotaSettings> fromNamespaceQuotas(final String namespace, final Quotas quotas) {
+ return fromQuotas(null, null, namespace, quotas);
+ }
+
+ private static List<QuotaSettings> fromQuotas(final String userName, final TableName tableName,
+ final String namespace, final Quotas quotas) {
+ List<QuotaSettings> settings = new ArrayList<QuotaSettings>();
+ if (quotas.hasThrottle()) {
+ settings.addAll(fromThrottle(userName, tableName, namespace, quotas.getThrottle()));
+ }
+ if (quotas.getBypassGlobals() == true) {
+ settings.add(new QuotaGlobalsSettingsBypass(userName, tableName, namespace, true));
+ }
+ return settings;
+ }
+
+ private static List<QuotaSettings> fromThrottle(final String userName, final TableName tableName,
+ final String namespace, final QuotaProtos.Throttle throttle) {
+ List<QuotaSettings> settings = new ArrayList<QuotaSettings>();
+ if (throttle.hasReqNum()) {
+ settings.add(ThrottleSettings.fromTimedQuota(userName, tableName, namespace,
+ ThrottleType.REQUEST_NUMBER, throttle.getReqNum()));
+ }
+ if (throttle.hasReqSize()) {
+ settings.add(ThrottleSettings.fromTimedQuota(userName, tableName, namespace,
+ ThrottleType.REQUEST_SIZE, throttle.getReqSize()));
+ }
+ return settings;
+ }
+
+ /* ==========================================================================
+ * RPC Throttle
+ */
+
+ /**
+ * Throttle the specified user.
+ *
+ * @param userName the user to throttle
+ * @param type the type of throttling
+ * @param limit the allowed number of request/data per timeUnit
+ * @param timeUnit the limit time unit
+ * @return the quota settings
+ */
+ public static QuotaSettings throttleUser(final String userName, final ThrottleType type,
+ final long limit, final TimeUnit timeUnit) {
+ return throttle(userName, null, null, type, limit, timeUnit);
+ }
+
+ /**
+ * Throttle the specified user on the specified table.
+ *
+ * @param userName the user to throttle
+ * @param tableName the table to throttle
+ * @param type the type of throttling
+ * @param limit the allowed number of request/data per timeUnit
+ * @param timeUnit the limit time unit
+ * @return the quota settings
+ */
+ public static QuotaSettings throttleUser(final String userName, final TableName tableName,
+ final ThrottleType type, final long limit, final TimeUnit timeUnit) {
+ return throttle(userName, tableName, null, type, limit, timeUnit);
+ }
+
+ /**
+ * Throttle the specified user on the specified namespace.
+ *
+ * @param userName the user to throttle
+ * @param namespace the namespace to throttle
+ * @param type the type of throttling
+ * @param limit the allowed number of request/data per timeUnit
+ * @param timeUnit the limit time unit
+ * @return the quota settings
+ */
+ public static QuotaSettings throttleUser(final String userName, final String namespace,
+ final ThrottleType type, final long limit, final TimeUnit timeUnit) {
+ return throttle(userName, null, namespace, type, limit, timeUnit);
+ }
+
+ /**
+ * Remove the throttling for the specified user.
+ *
+ * @param userName the user
+ * @return the quota settings
+ */
+ public static QuotaSettings unthrottleUser(final String userName) {
+ return throttle(userName, null, null, null, 0, null);
+ }
+
+ /**
+ * Remove the throttling for the specified user on the specified table.
+ *
+ * @param userName the user
+ * @param tableName the table
+ * @return the quota settings
+ */
+ public static QuotaSettings unthrottleUser(final String userName, final TableName tableName) {
+ return throttle(userName, tableName, null, null, 0, null);
+ }
+
+ /**
+ * Remove the throttling for the specified user on the specified namespace.
+ *
+ * @param userName the user
+ * @param namespace the namespace
+ * @return the quota settings
+ */
+ public static QuotaSettings unthrottleUser(final String userName, final String namespace) {
+ return throttle(userName, null, namespace, null, 0, null);
+ }
+
+ /**
+ * Throttle the specified table.
+ *
+ * @param tableName the table to throttle
+ * @param type the type of throttling
+ * @param limit the allowed number of request/data per timeUnit
+ * @param timeUnit the limit time unit
+ * @return the quota settings
+ */
+ public static QuotaSettings throttleTable(final TableName tableName, final ThrottleType type,
+ final long limit, final TimeUnit timeUnit) {
+ return throttle(null, tableName, null, type, limit, timeUnit);
+ }
+
+ /**
+ * Remove the throttling for the specified table.
+ *
+ * @param tableName the table
+ * @return the quota settings
+ */
+ public static QuotaSettings unthrottleTable(final TableName tableName) {
+ return throttle(null, tableName, null, null, 0, null);
+ }
+
+ /**
+ * Throttle the specified namespace.
+ *
+ * @param namespace the namespace to throttle
+ * @param type the type of throttling
+ * @param limit the allowed number of request/data per timeUnit
+ * @param timeUnit the limit time unit
+ * @return the quota settings
+ */
+ public static QuotaSettings throttleNamespace(final String namespace, final ThrottleType type,
+ final long limit, final TimeUnit timeUnit) {
+ return throttle(null, null, namespace, type, limit, timeUnit);
+ }
+
+ /**
+ * Remove the throttling for the specified namespace.
+ *
+ * @param namespace the namespace
+ * @return the quota settings
+ */
+ public static QuotaSettings unthrottleNamespace(final String namespace) {
+ return throttle(null, null, namespace, null, 0, null);
+ }
+
+ /* Throttle helper */
+ private static QuotaSettings throttle(final String userName, final TableName tableName,
+ final String namespace, final ThrottleType type, final long limit,
+ final TimeUnit timeUnit) {
+ QuotaProtos.ThrottleRequest.Builder builder = QuotaProtos.ThrottleRequest.newBuilder();
+ if (type != null) {
+ builder.setType(ProtobufUtil.toProtoThrottleType(type));
+ }
+ if (timeUnit != null) {
+ builder.setTimedQuota(ProtobufUtil.toTimedQuota(limit, timeUnit, QuotaScope.MACHINE));
+ }
+ return new ThrottleSettings(userName, tableName, namespace, builder.build());
+ }
+
+ /* ==========================================================================
+ * Global Settings
+ */
+
+ /**
+ * Set the "bypass global settings" for the specified user
+ *
+ * @param userName the user to throttle
+ * @param bypassGlobals true if the global settings should be bypassed
+ * @return the quota settings
+ */
+ public static QuotaSettings bypassGlobals(final String userName, final boolean bypassGlobals) {
+ return new QuotaGlobalsSettingsBypass(userName, null, null, bypassGlobals);
+ }
+}
http://git-wip-us.apache.org/repos/asf/hbase/blob/bd8df9cc/hbase-client/src/main/java/org/apache/hadoop/hbase/quotas/QuotaTableUtil.java
----------------------------------------------------------------------
diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/quotas/QuotaTableUtil.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/quotas/QuotaTableUtil.java
new file mode 100644
index 0000000..0618bc2
--- /dev/null
+++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/quotas/QuotaTableUtil.java
@@ -0,0 +1,418 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hadoop.hbase.quotas;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Pattern;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.hadoop.classification.InterfaceAudience;
+import org.apache.hadoop.classification.InterfaceStability;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.hbase.NamespaceDescriptor;
+import org.apache.hadoop.hbase.TableName;
+import org.apache.hadoop.hbase.client.HTable;
+import org.apache.hadoop.hbase.client.Get;
+import org.apache.hadoop.hbase.client.Result;
+import org.apache.hadoop.hbase.client.Scan;
+import org.apache.hadoop.hbase.filter.CompareFilter;
+import org.apache.hadoop.hbase.filter.Filter;
+import org.apache.hadoop.hbase.filter.FilterList;
+import org.apache.hadoop.hbase.filter.QualifierFilter;
+import org.apache.hadoop.hbase.filter.RowFilter;
+import org.apache.hadoop.hbase.filter.RegexStringComparator;
+import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
+import org.apache.hadoop.hbase.protobuf.generated.QuotaProtos.Quotas;
+import org.apache.hadoop.hbase.util.Bytes;
+import org.apache.hadoop.hbase.util.Strings;
+
+/**
+ * Helper class to interact with the quota table.
+ * <pre>
+ * ROW-KEY FAM/QUAL DATA
+ * n.<namespace> q:s <global-quotas>
+ * t.<table> q:s <global-quotas>
+ * u.<user> q:s <global-quotas>
+ * u.<user> q:s.<table> <table-quotas>
+ * u.<user> q:s.<ns>: <namespace-quotas>
+ * </pre>
+ */
+@InterfaceAudience.Private
+@InterfaceStability.Evolving
+public class QuotaTableUtil {
+ private static final Log LOG = LogFactory.getLog(QuotaTableUtil.class);
+
+ /** System table for quotas */
+ public static final TableName QUOTA_TABLE_NAME =
+ TableName.valueOf(NamespaceDescriptor.SYSTEM_NAMESPACE_NAME_STR, "quota");
+
+ protected static final byte[] QUOTA_FAMILY_INFO = Bytes.toBytes("q");
+ protected static final byte[] QUOTA_FAMILY_USAGE = Bytes.toBytes("u");
+ protected static final byte[] QUOTA_QUALIFIER_SETTINGS = Bytes.toBytes("s");
+ protected static final byte[] QUOTA_QUALIFIER_SETTINGS_PREFIX = Bytes.toBytes("s.");
+ protected static final byte[] QUOTA_USER_ROW_KEY_PREFIX = Bytes.toBytes("u.");
+ protected static final byte[] QUOTA_TABLE_ROW_KEY_PREFIX = Bytes.toBytes("t.");
+ protected static final byte[] QUOTA_NAMESPACE_ROW_KEY_PREFIX = Bytes.toBytes("n.");
+
+ /* =========================================================================
+ * Quota "settings" helpers
+ */
+ public static Quotas getTableQuota(final Configuration conf, final TableName table)
+ throws IOException {
+ return getQuotas(conf, getTableRowKey(table));
+ }
+
+ public static Quotas getNamespaceQuota(final Configuration conf, final String namespace)
+ throws IOException {
+ return getQuotas(conf, getNamespaceRowKey(namespace));
+ }
+
+ public static Quotas getUserQuota(final Configuration conf, final String user)
+ throws IOException {
+ return getQuotas(conf, getUserRowKey(user));
+ }
+
+ public static Quotas getUserQuota(final Configuration conf, final String user,
+ final TableName table) throws IOException {
+ return getQuotas(conf, getUserRowKey(user), getSettingsQualifierForUserTable(table));
+ }
+
+ public static Quotas getUserQuota(final Configuration conf, final String user,
+ final String namespace) throws IOException {
+ return getQuotas(conf, getUserRowKey(user), getSettingsQualifierForUserNamespace(namespace));
+ }
+
+ private static Quotas getQuotas(final Configuration conf, final byte[] rowKey)
+ throws IOException {
+ return getQuotas(conf, rowKey, QUOTA_QUALIFIER_SETTINGS);
+ }
+
+ private static Quotas getQuotas(final Configuration conf, final byte[] rowKey,
+ final byte[] qualifier) throws IOException {
+ Get get = new Get(rowKey);
+ get.addColumn(QUOTA_FAMILY_INFO, qualifier);
+ Result result = doGet(conf, get);
+ if (result.isEmpty()) {
+ return null;
+ }
+ return quotasFromData(result.getValue(QUOTA_FAMILY_INFO, qualifier));
+ }
+
+ public static Get makeGetForTableQuotas(final TableName table) {
+ Get get = new Get(getTableRowKey(table));
+ get.addFamily(QUOTA_FAMILY_INFO);
+ return get;
+ }
+
+ public static Get makeGetForNamespaceQuotas(final String namespace) {
+ Get get = new Get(getNamespaceRowKey(namespace));
+ get.addFamily(QUOTA_FAMILY_INFO);
+ return get;
+ }
+
+ public static Get makeGetForUserQuotas(final String user, final Iterable<TableName> tables,
+ final Iterable<String> namespaces) {
+ Get get = new Get(getUserRowKey(user));
+ get.addColumn(QUOTA_FAMILY_INFO, QUOTA_QUALIFIER_SETTINGS);
+ for (final TableName table: tables) {
+ get.addColumn(QUOTA_FAMILY_INFO, getSettingsQualifierForUserTable(table));
+ }
+ for (final String ns: namespaces) {
+ get.addColumn(QUOTA_FAMILY_INFO, getSettingsQualifierForUserNamespace(ns));
+ }
+ return get;
+ }
+
+ public static Scan makeScan(final QuotaFilter filter) {
+ Scan scan = new Scan();
+ scan.addFamily(QUOTA_FAMILY_INFO);
+ if (filter != null && !filter.isNull()) {
+ scan.setFilter(makeFilter(filter));
+ }
+ return scan;
+ }
+
+ /**
+ * converts quotafilter to serializeable filterlists.
+ */
+ public static Filter makeFilter(final QuotaFilter filter) {
+ FilterList filterList = new FilterList(FilterList.Operator.MUST_PASS_ALL);
+ if (!Strings.isEmpty(filter.getUserFilter())) {
+ FilterList userFilters = new FilterList(FilterList.Operator.MUST_PASS_ONE);
+ boolean hasFilter = false;
+
+ if (!Strings.isEmpty(filter.getNamespaceFilter())) {
+ FilterList nsFilters = new FilterList(FilterList.Operator.MUST_PASS_ALL);
+ nsFilters.addFilter(new RowFilter(CompareFilter.CompareOp.EQUAL,
+ new RegexStringComparator(getUserRowKeyRegex(filter.getUserFilter()), 0)));
+ nsFilters.addFilter(new QualifierFilter(CompareFilter.CompareOp.EQUAL,
+ new RegexStringComparator(
+ getSettingsQualifierRegexForUserNamespace(filter.getNamespaceFilter()), 0)));
+ userFilters.addFilter(nsFilters);
+ hasFilter = true;
+ }
+ if (!Strings.isEmpty(filter.getTableFilter())) {
+ FilterList tableFilters = new FilterList(FilterList.Operator.MUST_PASS_ALL);
+ tableFilters.addFilter(new RowFilter(CompareFilter.CompareOp.EQUAL,
+ new RegexStringComparator(getUserRowKeyRegex(filter.getUserFilter()), 0)));
+ tableFilters.addFilter(new QualifierFilter(CompareFilter.CompareOp.EQUAL,
+ new RegexStringComparator(
+ getSettingsQualifierRegexForUserTable(filter.getTableFilter()), 0)));
+ userFilters.addFilter(tableFilters);
+ hasFilter = true;
+ }
+ if (!hasFilter) {
+ userFilters.addFilter(new RowFilter(CompareFilter.CompareOp.EQUAL,
+ new RegexStringComparator(getUserRowKeyRegex(filter.getUserFilter()), 0)));
+ }
+
+ filterList.addFilter(userFilters);
+ } else if (!Strings.isEmpty(filter.getTableFilter())) {
+ filterList.addFilter(new RowFilter(CompareFilter.CompareOp.EQUAL,
+ new RegexStringComparator(getTableRowKeyRegex(filter.getTableFilter()), 0)));
+ } else if (!Strings.isEmpty(filter.getNamespaceFilter())) {
+ filterList.addFilter(new RowFilter(CompareFilter.CompareOp.EQUAL,
+ new RegexStringComparator(getNamespaceRowKeyRegex(filter.getNamespaceFilter()), 0)));
+ }
+ return filterList;
+ }
+
+ public static interface UserQuotasVisitor {
+ void visitUserQuotas(final String userName, final Quotas quotas)
+ throws IOException;
+ void visitUserQuotas(final String userName, final TableName table, final Quotas quotas)
+ throws IOException;
+ void visitUserQuotas(final String userName, final String namespace, final Quotas quotas)
+ throws IOException;
+ }
+
+ public static interface TableQuotasVisitor {
+ void visitTableQuotas(final TableName tableName, final Quotas quotas)
+ throws IOException;
+ }
+
+ public static interface NamespaceQuotasVisitor {
+ void visitNamespaceQuotas(final String namespace, final Quotas quotas)
+ throws IOException;
+ }
+
+ public static interface QuotasVisitor extends UserQuotasVisitor,
+ TableQuotasVisitor, NamespaceQuotasVisitor {
+ }
+
+ public static void parseResult(final Result result, final QuotasVisitor visitor)
+ throws IOException {
+ byte[] row = result.getRow();
+ if (isNamespaceRowKey(row)) {
+ parseNamespaceResult(result, visitor);
+ } else if (isTableRowKey(row)) {
+ parseTableResult(result, visitor);
+ } else if (isUserRowKey(row)) {
+ parseUserResult(result, visitor);
+ } else {
+ LOG.warn("unexpected row-key: " + Bytes.toString(row));
+ }
+ }
+
+ public static void parseNamespaceResult(final Result result,
+ final NamespaceQuotasVisitor visitor) throws IOException {
+ String namespace = getNamespaceFromRowKey(result.getRow());
+ parseNamespaceResult(namespace, result, visitor);
+ }
+
+ protected static void parseNamespaceResult(final String namespace, final Result result,
+ final NamespaceQuotasVisitor visitor) throws IOException {
+ byte[] data = result.getValue(QUOTA_FAMILY_INFO, QUOTA_QUALIFIER_SETTINGS);
+ if (data != null) {
+ Quotas quotas = quotasFromData(data);
+ visitor.visitNamespaceQuotas(namespace, quotas);
+ }
+ }
+
+ public static void parseTableResult(final Result result, final TableQuotasVisitor visitor)
+ throws IOException {
+ TableName table = getTableFromRowKey(result.getRow());
+ parseTableResult(table, result, visitor);
+ }
+
+ protected static void parseTableResult(final TableName table, final Result result,
+ final TableQuotasVisitor visitor) throws IOException {
+ byte[] data = result.getValue(QUOTA_FAMILY_INFO, QUOTA_QUALIFIER_SETTINGS);
+ if (data != null) {
+ Quotas quotas = quotasFromData(data);
+ visitor.visitTableQuotas(table, quotas);
+ }
+ }
+
+ public static void parseUserResult(final Result result, final UserQuotasVisitor visitor)
+ throws IOException {
+ String userName = getUserFromRowKey(result.getRow());
+ parseUserResult(userName, result, visitor);
+ }
+
+ protected static void parseUserResult(final String userName, final Result result,
+ final UserQuotasVisitor visitor) throws IOException {
+ Map<byte[], byte[]> familyMap = result.getFamilyMap(QUOTA_FAMILY_INFO);
+ if (familyMap == null || familyMap.isEmpty()) return;
+
+ for (Map.Entry<byte[], byte[]> entry: familyMap.entrySet()) {
+ Quotas quotas = quotasFromData(entry.getValue());
+ if (Bytes.startsWith(entry.getKey(), QUOTA_QUALIFIER_SETTINGS_PREFIX)) {
+ String name = Bytes.toString(entry.getKey(), QUOTA_QUALIFIER_SETTINGS_PREFIX.length);
+ if (name.charAt(name.length() - 1) == TableName.NAMESPACE_DELIM) {
+ String namespace = name.substring(0, name.length() - 1);
+ visitor.visitUserQuotas(userName, namespace, quotas);
+ } else {
+ TableName table = TableName.valueOf(name);
+ visitor.visitUserQuotas(userName, table, quotas);
+ }
+ } else if (Bytes.equals(entry.getKey(), QUOTA_QUALIFIER_SETTINGS)) {
+ visitor.visitUserQuotas(userName, quotas);
+ }
+ }
+ }
+
+ /* =========================================================================
+ * Quotas protobuf helpers
+ */
+ protected static Quotas quotasFromData(final byte[] data) throws IOException {
+ int magicLen = ProtobufUtil.lengthOfPBMagic();
+ if (!ProtobufUtil.isPBMagicPrefix(data, 0, magicLen)) {
+ throw new IOException("Missing pb magic prefix");
+ }
+ return Quotas.parseFrom(new ByteArrayInputStream(data, magicLen, data.length - magicLen));
+ }
+
+ protected static byte[] quotasToData(final Quotas data) throws IOException {
+ ByteArrayOutputStream stream = new ByteArrayOutputStream();
+ stream.write(ProtobufUtil.PB_MAGIC);
+ data.writeTo(stream);
+ return stream.toByteArray();
+ }
+
+ public static boolean isEmptyQuota(final Quotas quotas) {
+ boolean hasSettings = false;
+ hasSettings |= quotas.hasThrottle();
+ hasSettings |= quotas.hasBypassGlobals();
+ return !hasSettings;
+ }
+
+ /* =========================================================================
+ * HTable helpers
+ */
+ protected static Result doGet(final Configuration conf, final Get get)
+ throws IOException {
+ HTable table = new HTable(conf, QUOTA_TABLE_NAME);
+ try {
+ return table.get(get);
+ } finally {
+ table.close();
+ }
+ }
+
+ protected static Result[] doGet(final Configuration conf, final List<Get> gets)
+ throws IOException {
+ HTable table = new HTable(conf, QUOTA_TABLE_NAME);
+ try {
+ return table.get(gets);
+ } finally {
+ table.close();
+ }
+ }
+
+ /* =========================================================================
+ * Quota table row key helpers
+ */
+ protected static byte[] getUserRowKey(final String user) {
+ return Bytes.add(QUOTA_USER_ROW_KEY_PREFIX, Bytes.toBytes(user));
+ }
+
+ protected static byte[] getTableRowKey(final TableName table) {
+ return Bytes.add(QUOTA_TABLE_ROW_KEY_PREFIX, table.getName());
+ }
+
+ protected static byte[] getNamespaceRowKey(final String namespace) {
+ return Bytes.add(QUOTA_NAMESPACE_ROW_KEY_PREFIX, Bytes.toBytes(namespace));
+ }
+
+ protected static byte[] getSettingsQualifierForUserTable(final TableName tableName) {
+ return Bytes.add(QUOTA_QUALIFIER_SETTINGS_PREFIX, tableName.getName());
+ }
+
+ protected static byte[] getSettingsQualifierForUserNamespace(final String namespace) {
+ return Bytes.add(QUOTA_QUALIFIER_SETTINGS_PREFIX,
+ Bytes.toBytes(namespace + TableName.NAMESPACE_DELIM));
+ }
+
+ protected static String getUserRowKeyRegex(final String user) {
+ return getRowKeyRegEx(QUOTA_USER_ROW_KEY_PREFIX, user);
+ }
+
+ protected static String getTableRowKeyRegex(final String table) {
+ return getRowKeyRegEx(QUOTA_TABLE_ROW_KEY_PREFIX, table);
+ }
+
+ protected static String getNamespaceRowKeyRegex(final String namespace) {
+ return getRowKeyRegEx(QUOTA_NAMESPACE_ROW_KEY_PREFIX, namespace);
+ }
+
+ private static String getRowKeyRegEx(final byte[] prefix, final String regex) {
+ return '^' + Pattern.quote(Bytes.toString(prefix)) + regex + '$';
+ }
+
+ protected static String getSettingsQualifierRegexForUserTable(final String table) {
+ return '^' + Pattern.quote(Bytes.toString(QUOTA_QUALIFIER_SETTINGS_PREFIX)) +
+ table + "(?<!" + Pattern.quote(Character.toString(TableName.NAMESPACE_DELIM)) + ")$";
+ }
+
+ protected static String getSettingsQualifierRegexForUserNamespace(final String namespace) {
+ return '^' + Pattern.quote(Bytes.toString(QUOTA_QUALIFIER_SETTINGS_PREFIX)) +
+ namespace + Pattern.quote(Character.toString(TableName.NAMESPACE_DELIM)) + '$';
+ }
+
+ protected static boolean isNamespaceRowKey(final byte[] key) {
+ return Bytes.startsWith(key, QUOTA_NAMESPACE_ROW_KEY_PREFIX);
+ }
+
+ protected static String getNamespaceFromRowKey(final byte[] key) {
+ return Bytes.toString(key, QUOTA_NAMESPACE_ROW_KEY_PREFIX.length);
+ }
+
+ protected static boolean isTableRowKey(final byte[] key) {
+ return Bytes.startsWith(key, QUOTA_TABLE_ROW_KEY_PREFIX);
+ }
+
+ protected static TableName getTableFromRowKey(final byte[] key) {
+ return TableName.valueOf(Bytes.toString(key, QUOTA_TABLE_ROW_KEY_PREFIX.length));
+ }
+
+ protected static boolean isUserRowKey(final byte[] key) {
+ return Bytes.startsWith(key, QUOTA_USER_ROW_KEY_PREFIX);
+ }
+
+ protected static String getUserFromRowKey(final byte[] key) {
+ return Bytes.toString(key, QUOTA_USER_ROW_KEY_PREFIX.length);
+ }
+}
http://git-wip-us.apache.org/repos/asf/hbase/blob/bd8df9cc/hbase-client/src/main/java/org/apache/hadoop/hbase/quotas/QuotaType.java
----------------------------------------------------------------------
diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/quotas/QuotaType.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/quotas/QuotaType.java
new file mode 100644
index 0000000..97450ba
--- /dev/null
+++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/quotas/QuotaType.java
@@ -0,0 +1,31 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.hadoop.hbase.quotas;
+
+import org.apache.hadoop.classification.InterfaceAudience;
+import org.apache.hadoop.classification.InterfaceStability;
+
+/**
+ * Describe the Quota Type.
+ */
+@InterfaceAudience.Public
+@InterfaceStability.Evolving
+public enum QuotaType {
+ THROTTLE,
+ GLOBAL_BYPASS,
+}
http://git-wip-us.apache.org/repos/asf/hbase/blob/bd8df9cc/hbase-client/src/main/java/org/apache/hadoop/hbase/quotas/ThrottleSettings.java
----------------------------------------------------------------------
diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/quotas/ThrottleSettings.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/quotas/ThrottleSettings.java
new file mode 100644
index 0000000..395365b
--- /dev/null
+++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/quotas/ThrottleSettings.java
@@ -0,0 +1,109 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.hadoop.hbase.quotas;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.hadoop.classification.InterfaceAudience;
+import org.apache.hadoop.classification.InterfaceStability;
+import org.apache.hadoop.hbase.TableName;
+import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
+import org.apache.hadoop.hbase.protobuf.generated.QuotaProtos;
+import org.apache.hadoop.hbase.protobuf.generated.QuotaProtos.Quotas;
+import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.SetQuotaRequest;
+
+@InterfaceAudience.Private
+@InterfaceStability.Evolving
+class ThrottleSettings extends QuotaSettings {
+ private final QuotaProtos.ThrottleRequest proto;
+
+ ThrottleSettings(final String userName, final TableName tableName,
+ final String namespace, final QuotaProtos.ThrottleRequest proto) {
+ super(userName, tableName, namespace);
+ this.proto = proto;
+ }
+
+ public ThrottleType getThrottleType() {
+ return ProtobufUtil.toThrottleType(proto.getType());
+ }
+
+ public long getSoftLimit() {
+ return proto.hasTimedQuota() ? proto.getTimedQuota().getSoftLimit() : -1;
+ }
+
+ public TimeUnit getTimeUnit() {
+ return proto.hasTimedQuota() ?
+ ProtobufUtil.toTimeUnit(proto.getTimedQuota().getTimeUnit()) : null;
+ }
+
+ @Override
+ public QuotaType getQuotaType() {
+ return QuotaType.THROTTLE;
+ }
+
+ @Override
+ protected void setupSetQuotaRequest(SetQuotaRequest.Builder builder) {
+ builder.setThrottle(proto);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("TYPE => THROTTLE");
+ if (proto.hasType()) {
+ builder.append(", THROTTLE_TYPE => ");
+ builder.append(proto.getType().toString());
+ }
+ if (proto.hasTimedQuota()) {
+ QuotaProtos.TimedQuota timedQuota = proto.getTimedQuota();
+ builder.append(", LIMIT => ");
+ if (timedQuota.hasSoftLimit()) {
+ switch (getThrottleType()) {
+ case REQUEST_NUMBER:
+ builder.append(String.format("%dreq", timedQuota.getSoftLimit()));
+ break;
+ case REQUEST_SIZE:
+ builder.append(sizeToString(timedQuota.getSoftLimit()));
+ break;
+ }
+ } else if (timedQuota.hasShare()) {
+ builder.append(String.format("%.2f%%", timedQuota.getShare()));
+ }
+ builder.append('/');
+ builder.append(timeToString(ProtobufUtil.toTimeUnit(timedQuota.getTimeUnit())));
+ if (timedQuota.hasScope()) {
+ builder.append(", SCOPE => ");
+ builder.append(timedQuota.getScope().toString());
+ }
+ } else {
+ builder.append(", LIMIT => NONE");
+ }
+ return builder.toString();
+ }
+
+ static ThrottleSettings fromTimedQuota(final String userName,
+ final TableName tableName, final String namespace,
+ ThrottleType type, QuotaProtos.TimedQuota timedQuota) {
+ QuotaProtos.ThrottleRequest.Builder builder = QuotaProtos.ThrottleRequest.newBuilder();
+ builder.setType(ProtobufUtil.toProtoThrottleType(type));
+ builder.setTimedQuota(timedQuota);
+ return new ThrottleSettings(userName, tableName, namespace, builder.build());
+ }
+}
http://git-wip-us.apache.org/repos/asf/hbase/blob/bd8df9cc/hbase-client/src/main/java/org/apache/hadoop/hbase/quotas/ThrottleType.java
----------------------------------------------------------------------
diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/quotas/ThrottleType.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/quotas/ThrottleType.java
new file mode 100644
index 0000000..0778039
--- /dev/null
+++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/quotas/ThrottleType.java
@@ -0,0 +1,34 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.hadoop.hbase.quotas;
+
+import org.apache.hadoop.classification.InterfaceAudience;
+import org.apache.hadoop.classification.InterfaceStability;
+
+/**
+ * Describe the Throttle Type.
+ */
+@InterfaceAudience.Public
+@InterfaceStability.Evolving
+public enum ThrottleType {
+ /** Throttling based on the number of request per time-unit */
+ REQUEST_NUMBER,
+
+ /** Throttling based on the read+write data size */
+ REQUEST_SIZE,
+}
http://git-wip-us.apache.org/repos/asf/hbase/blob/bd8df9cc/hbase-client/src/main/java/org/apache/hadoop/hbase/quotas/ThrottlingException.java
----------------------------------------------------------------------
diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/quotas/ThrottlingException.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/quotas/ThrottlingException.java
new file mode 100644
index 0000000..bb24f86
--- /dev/null
+++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/quotas/ThrottlingException.java
@@ -0,0 +1,166 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hadoop.hbase.quotas;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.hadoop.util.StringUtils;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Describe the throttling result.
+ *
+ * TODO: At some point this will be handled on the client side to prevent
+ * operation to go on the server if the waitInterval is grater than the one got
+ * as result of this exception.
+ */
+public class ThrottlingException extends QuotaExceededException {
+ private static final long serialVersionUID = 1406576492085155743L;
+
+ private static final Log LOG = LogFactory.getLog(ThrottlingException.class);
+
+ public enum Type {
+ NumRequestsExceeded,
+ NumReadRequestsExceeded,
+ NumWriteRequestsExceeded,
+ WriteSizeExceeded,
+ ReadSizeExceeded,
+ }
+
+ private static final String[] MSG_TYPE = new String[] {
+ "number of requests exceeded",
+ "number of read requests exceeded",
+ "number of write requests exceeded",
+ "write size limit exceeded",
+ "read size limit exceeded",
+ };
+
+ private static final String MSG_WAIT = " - wait ";
+
+ private long waitInterval;
+ private Type type;
+
+ public ThrottlingException(String msg) {
+ super(msg);
+
+ // Dirty workaround to get the information after
+ // ((RemoteException)e.getCause()).unwrapRemoteException()
+ for (int i = 0; i < MSG_TYPE.length; ++i) {
+ int index = msg.indexOf(MSG_TYPE[i]);
+ if (index >= 0) {
+ String waitTimeStr = msg.substring(index + MSG_TYPE[i].length() + MSG_WAIT.length());
+ type = Type.values()[i];;
+ waitInterval = timeFromString(waitTimeStr);
+ break;
+ }
+ }
+ }
+
+ public ThrottlingException(final Type type, final long waitInterval, final String msg) {
+ super(msg);
+ this.waitInterval = waitInterval;
+ this.type = type;
+ }
+
+ public Type getType() {
+ return this.type;
+ }
+
+ public long getWaitInterval() {
+ return this.waitInterval;
+ }
+
+ public static void throwNumRequestsExceeded(final long waitInterval)
+ throws ThrottlingException {
+ throwThrottlingException(Type.NumRequestsExceeded, waitInterval);
+ }
+
+ public static void throwNumReadRequestsExceeded(final long waitInterval)
+ throws ThrottlingException {
+ throwThrottlingException(Type.NumReadRequestsExceeded, waitInterval);
+ }
+
+ public static void throwNumWriteRequestsExceeded(final long waitInterval)
+ throws ThrottlingException {
+ throwThrottlingException(Type.NumWriteRequestsExceeded, waitInterval);
+ }
+
+ public static void throwWriteSizeExceeded(final long waitInterval)
+ throws ThrottlingException {
+ throwThrottlingException(Type.WriteSizeExceeded, waitInterval);
+ }
+
+ public static void throwReadSizeExceeded(final long waitInterval)
+ throws ThrottlingException {
+ throwThrottlingException(Type.ReadSizeExceeded, waitInterval);
+ }
+
+ private static void throwThrottlingException(final Type type, final long waitInterval)
+ throws ThrottlingException {
+ String msg = MSG_TYPE[type.ordinal()] + MSG_WAIT + formatTime(waitInterval);
+ throw new ThrottlingException(type, waitInterval, msg);
+ }
+
+ public static String formatTime(long timeDiff) {
+ StringBuilder buf = new StringBuilder();
+ long hours = timeDiff / (60*60*1000);
+ long rem = (timeDiff % (60*60*1000));
+ long minutes = rem / (60*1000);
+ rem = rem % (60*1000);
+ float seconds = rem / 1000.0f;
+
+ if (hours != 0){
+ buf.append(hours);
+ buf.append("hrs, ");
+ }
+ if (minutes != 0){
+ buf.append(minutes);
+ buf.append("mins, ");
+ }
+ buf.append(String.format("%.2fsec", seconds));
+ return buf.toString();
+ }
+
+ private static long timeFromString(String timeDiff) {
+ Pattern[] patterns = new Pattern[] {
+ Pattern.compile("^(\\d+\\.\\d\\d)sec"),
+ Pattern.compile("^(\\d+)mins, (\\d+\\.\\d\\d)sec"),
+ Pattern.compile("^(\\d+)hrs, (\\d+)mins, (\\d+\\.\\d\\d)sec")
+ };
+
+ for (int i = 0; i < patterns.length; ++i) {
+ Matcher m = patterns[i].matcher(timeDiff);
+ if (m.find()) {
+ long time = Math.round(Float.parseFloat(m.group(1 + i)) * 1000);
+ if (i > 0) {
+ time += Long.parseLong(m.group(i)) * (60 * 1000);
+ }
+ if (i > 1) {
+ time += Long.parseLong(m.group(i - 1)) * (60 * 60 * 1000);
+ }
+ return time;
+ }
+ }
+
+ return -1;
+ }
+}
http://git-wip-us.apache.org/repos/asf/hbase/blob/bd8df9cc/hbase-client/src/main/java/org/apache/hadoop/hbase/util/Sleeper.java
----------------------------------------------------------------------
diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/util/Sleeper.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/util/Sleeper.java
index c9f9e6c..8f9979b 100644
--- a/hbase-client/src/main/java/org/apache/hadoop/hbase/util/Sleeper.java
+++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/util/Sleeper.java
@@ -112,4 +112,11 @@ public class Sleeper {
}
triggerWake = false;
}
+
+ /**
+ * @return the sleep period in milliseconds
+ */
+ public final int getPeriod() {
+ return period;
+ }
}
http://git-wip-us.apache.org/repos/asf/hbase/blob/bd8df9cc/hbase-common/src/main/java/org/apache/hadoop/hbase/util/Bytes.java
----------------------------------------------------------------------
diff --git a/hbase-common/src/main/java/org/apache/hadoop/hbase/util/Bytes.java b/hbase-common/src/main/java/org/apache/hadoop/hbase/util/Bytes.java
index 44d13a5..84a5819 100644
--- a/hbase-common/src/main/java/org/apache/hadoop/hbase/util/Bytes.java
+++ b/hbase-common/src/main/java/org/apache/hadoop/hbase/util/Bytes.java
@@ -128,7 +128,7 @@ public class Bytes implements Comparable<Bytes> {
// SizeOf which uses java.lang.instrument says 24 bytes. (3 longs?)
public static final int ESTIMATED_HEAP_TAX = 16;
-
+
/**
* Returns length of the byte array, returning 0 if the array is null.
* Useful for calculating sizes.
@@ -556,6 +556,25 @@ public class Bytes implements Comparable<Bytes> {
*
* @param b Presumed UTF-8 encoded byte array.
* @param off offset into array
+ * @return String made from <code>b</code> or null
+ */
+ public static String toString(final byte [] b, int off) {
+ if (b == null) {
+ return null;
+ }
+ int len = b.length - off;
+ if (len <= 0) {
+ return "";
+ }
+ return new String(b, off, len, UTF8_CHARSET);
+ }
+
+ /**
+ * This method will convert utf8 encoded bytes into a string. If
+ * the given byte array is null, this method will return null.
+ *
+ * @param b Presumed UTF-8 encoded byte array.
+ * @param off offset into array
* @param len length of utf-8 sequence
* @return String made from <code>b</code> or null
*/
@@ -2285,7 +2304,7 @@ public class Bytes implements Comparable<Bytes> {
}
return result;
}
-
+
/**
* Convert a byte array into a hex string
* @param b
http://git-wip-us.apache.org/repos/asf/hbase/blob/bd8df9cc/hbase-protocol/pom.xml
----------------------------------------------------------------------
diff --git a/hbase-protocol/pom.xml b/hbase-protocol/pom.xml
index 850d0e5..297a7af 100644
--- a/hbase-protocol/pom.xml
+++ b/hbase-protocol/pom.xml
@@ -172,6 +172,7 @@
<include>MapReduce.proto</include>
<include>Master.proto</include>
<include>MultiRowMutation.proto</include>
+ <include>Quota.proto</include>
<include>RegionServerStatus.proto</include>
<include>RowProcessor.proto</include>
<include>RPC.proto</include>