You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hbase.apache.org by el...@apache.org on 2019/09/20 17:51:37 UTC

[hbase] 02/02: HBASE-22142 Drop table RegionSizes with namespace quota

This is an automated email from the ASF dual-hosted git repository.

elserj pushed a commit to branch branch-2
in repository https://gitbox.apache.org/repos/asf/hbase.git

commit f1d3b5442292a02faacf955feec00936d6731fc8
Author: shardul-cr7 <sh...@gmail.com>
AuthorDate: Fri Sep 20 10:57:15 2019 -0400

    HBASE-22142 Drop table RegionSizes with namespace quota
    
    There was a bug in which we would not drop the RegionSizes
    for a table in a namespace, where the namespace had a quota
    on it. This allowed a scenario in which recreation of a table
    inside of a namespace would unintentionally move into violation
    despite the table being empty. Need to make sure the RegionSizes
    are dropped on table deletion if there is _any_ quota applying
    to that table.
    
    Signed-off-by: Josh Elser <el...@apache.org>
---
 .../hadoop/hbase/quotas/MasterQuotaManager.java    |  9 ++++
 .../hadoop/hbase/quotas/MasterQuotasObserver.java  | 47 +++++++++++++------
 .../hbase/quotas/TestSpaceQuotaDropTable.java      | 52 ++++++++++++++++++++++
 3 files changed, 95 insertions(+), 13 deletions(-)

diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/quotas/MasterQuotaManager.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/quotas/MasterQuotaManager.java
index d17f1d5..c103c68 100644
--- a/hbase-server/src/main/java/org/apache/hadoop/hbase/quotas/MasterQuotaManager.java
+++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/quotas/MasterQuotaManager.java
@@ -709,5 +709,14 @@ public class MasterQuotaManager implements RegionStateListener {
     }
     return numEntriesRemoved;
   }
+
+  /**
+   * Removes each region size entry where the RegionInfo references the provided TableName.
+   *
+   * @param tableName tableName.
+   */
+  public void removeRegionSizesForTable(TableName tableName) {
+    regionSizes.keySet().removeIf(regionInfo -> regionInfo.getTable().equals(tableName));
+  }
 }
 
diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/quotas/MasterQuotasObserver.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/quotas/MasterQuotasObserver.java
index cad3129..1ddc9e3 100644
--- a/hbase-server/src/main/java/org/apache/hadoop/hbase/quotas/MasterQuotasObserver.java
+++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/quotas/MasterQuotasObserver.java
@@ -24,10 +24,14 @@ import org.apache.hadoop.hbase.CoprocessorEnvironment;
 import org.apache.hadoop.hbase.TableName;
 import org.apache.hadoop.hbase.client.Admin;
 import org.apache.hadoop.hbase.client.Connection;
+import org.apache.hadoop.hbase.coprocessor.CoprocessorException;
+import org.apache.hadoop.hbase.coprocessor.CoreCoprocessor;
+import org.apache.hadoop.hbase.coprocessor.HasMasterServices;
 import org.apache.hadoop.hbase.coprocessor.MasterCoprocessor;
 import org.apache.hadoop.hbase.coprocessor.MasterCoprocessorEnvironment;
 import org.apache.hadoop.hbase.coprocessor.MasterObserver;
 import org.apache.hadoop.hbase.coprocessor.ObserverContext;
+import org.apache.hadoop.hbase.master.MasterServices;
 import org.apache.yetus.audience.InterfaceAudience;
 import org.apache.hadoop.hbase.shaded.protobuf.generated.QuotaProtos.Quotas;
 
@@ -35,6 +39,7 @@ import org.apache.hadoop.hbase.shaded.protobuf.generated.QuotaProtos.Quotas;
  * An observer to automatically delete quotas when a table/namespace
  * is deleted.
  */
+@CoreCoprocessor
 @InterfaceAudience.Private
 public class MasterQuotasObserver implements MasterCoprocessor, MasterObserver {
   public static final String REMOVE_QUOTA_ON_TABLE_DELETE = "hbase.quota.remove.on.table.delete";
@@ -43,6 +48,7 @@ public class MasterQuotasObserver implements MasterCoprocessor, MasterObserver {
   private CoprocessorEnvironment cpEnv;
   private Configuration conf;
   private boolean quotasEnabled = false;
+  private MasterServices masterServices;
 
   @Override
   public Optional<MasterObserver> getMasterObserver() {
@@ -51,9 +57,19 @@ public class MasterQuotasObserver implements MasterCoprocessor, MasterObserver {
 
   @Override
   public void start(CoprocessorEnvironment ctx) throws IOException {
-    this.cpEnv = ctx;
-    this.conf = cpEnv.getConfiguration();
+    this.conf = ctx.getConfiguration();
     this.quotasEnabled = QuotaUtil.isQuotaEnabled(conf);
+
+    if (!(ctx instanceof MasterCoprocessorEnvironment)) {
+      throw new CoprocessorException("Must be loaded on master.");
+    }
+    // if running on master
+    MasterCoprocessorEnvironment mEnv = (MasterCoprocessorEnvironment) ctx;
+    if (mEnv instanceof HasMasterServices) {
+      this.masterServices = ((HasMasterServices) mEnv).getMasterServices();
+    } else {
+      throw new CoprocessorException("Must be loaded on a master having master services.");
+    }
   }
 
   @Override
@@ -64,18 +80,23 @@ public class MasterQuotasObserver implements MasterCoprocessor, MasterObserver {
       return;
     }
     final Connection conn = ctx.getEnvironment().getConnection();
-    Quotas quotas = QuotaUtil.getTableQuota(conn, tableName);
-    if (quotas != null){
-      if (quotas.hasSpace()){
-        QuotaSettings settings = QuotaSettingsFactory.removeTableSpaceLimit(tableName);
-        try (Admin admin = conn.getAdmin()) {
-          admin.setQuota(settings);
+    Quotas tableQuotas = QuotaUtil.getTableQuota(conn, tableName);
+    Quotas namespaceQuotas = QuotaUtil.getNamespaceQuota(conn, tableName.getNamespaceAsString());
+    if (tableQuotas != null || namespaceQuotas != null) {
+      // Remove regions of table from space quota map.
+      this.masterServices.getMasterQuotaManager().removeRegionSizesForTable(tableName);
+      if (tableQuotas != null) {
+        if (tableQuotas.hasSpace()) {
+          QuotaSettings settings = QuotaSettingsFactory.removeTableSpaceLimit(tableName);
+          try (Admin admin = conn.getAdmin()) {
+            admin.setQuota(settings);
+          }
         }
-      }
-      if (quotas.hasThrottle()){
-        QuotaSettings settings = QuotaSettingsFactory.unthrottleTable(tableName);
-        try (Admin admin = conn.getAdmin()) {
-          admin.setQuota(settings);
+        if (tableQuotas.hasThrottle()) {
+          QuotaSettings settings = QuotaSettingsFactory.unthrottleTable(tableName);
+          try (Admin admin = conn.getAdmin()) {
+            admin.setQuota(settings);
+          }
         }
       }
     }
diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/quotas/TestSpaceQuotaDropTable.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/quotas/TestSpaceQuotaDropTable.java
index cb60d12..11b9ec6 100644
--- a/hbase-server/src/test/java/org/apache/hadoop/hbase/quotas/TestSpaceQuotaDropTable.java
+++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/quotas/TestSpaceQuotaDropTable.java
@@ -15,16 +15,23 @@
  */
 package org.apache.hadoop.hbase.quotas;
 
+import java.util.List;
+import java.util.Map;
 import java.util.concurrent.atomic.AtomicLong;
 
 import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.hbase.HBaseClassTestRule;
 import org.apache.hadoop.hbase.HBaseTestingUtility;
+import org.apache.hadoop.hbase.MetaTableAccessor;
 import org.apache.hadoop.hbase.TableName;
+import org.apache.hadoop.hbase.Waiter;
 import org.apache.hadoop.hbase.client.Put;
+import org.apache.hadoop.hbase.client.RegionInfo;
+import org.apache.hadoop.hbase.master.HMaster;
 import org.apache.hadoop.hbase.testclassification.LargeTests;
 import org.apache.hadoop.hbase.util.Bytes;
 import org.junit.AfterClass;
+import org.junit.Assert;
 import org.junit.Before;
 import org.junit.BeforeClass;
 import org.junit.ClassRule;
@@ -87,6 +94,51 @@ public class TestSpaceQuotaDropTable {
     setQuotaAndThenDropTable(SpaceViolationPolicy.DISABLE);
   }
 
+  @Test
+  public void testSetQuotaAndThenDropTableWithRegionReport() throws Exception {
+    final TableName tn = helper.createTable();
+    helper.setQuotaLimit(tn, SpaceViolationPolicy.NO_INSERTS, 1L);
+    helper.writeData(tn, 2L);
+
+    final HMaster master = TEST_UTIL.getMiniHBaseCluster().getMaster();
+    final MasterQuotaManager quotaManager = master.getMasterQuotaManager();
+
+    // Make sure the master has report for the table.
+    Waiter.waitFor(TEST_UTIL.getConfiguration(), 30 * 1000, new Waiter.Predicate<Exception>() {
+      @Override
+      public boolean evaluate() throws Exception {
+        Map<RegionInfo, Long> regionSizes = quotaManager.snapshotRegionSizes();
+        List<RegionInfo> tableRegions =
+            MetaTableAccessor.getTableRegions(TEST_UTIL.getConnection(), tn);
+        return regionSizes.containsKey(tableRegions.get(0));
+      }
+    });
+
+    boolean hasRegionSize = false;
+
+    // region report should be present before dropping the table.
+    for (Map.Entry<RegionInfo, Long> entry : quotaManager.snapshotRegionSizes().entrySet()) {
+      if (entry.getKey().getTable().equals(tn)) {
+        hasRegionSize = true;
+        break;
+      }
+    }
+
+    // regionSize report for the given table should be present before dropping the table.
+    Assert.assertTrue(hasRegionSize);
+
+    // drop the table
+    TEST_UTIL.getAdmin().disableTable(tn);
+    TEST_UTIL.getAdmin().deleteTable(tn);
+
+    // check if deleted table region report still present in the map.
+    for (Map.Entry<RegionInfo, Long> entry : quotaManager.snapshotRegionSizes().entrySet()) {
+      if (entry.getKey().getTable().equals(tn)) {
+        Assert.fail("Dropped table regionSizes were not deleted during the drop command");
+      }
+    }
+  }
+
   private void setQuotaAndThenDropTable(SpaceViolationPolicy policy) throws Exception {
     Put put = new Put(Bytes.toBytes("to_reject"));
     put.addColumn(Bytes.toBytes(SpaceQuotaHelperForTests.F1), Bytes.toBytes("to"),