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>