You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hbase.apache.org by te...@apache.org on 2016/03/21 11:46:32 UTC

hbase git commit: HBASE-15433 SnapshotManager#restoreSnapshot not update table and region count quota correctly when encountering exception (Jianwei Cui)

Repository: hbase
Updated Branches:
  refs/heads/master f48c92d14 -> f1d453599


HBASE-15433 SnapshotManager#restoreSnapshot not update table and region count quota correctly when encountering exception (Jianwei Cui)


Project: http://git-wip-us.apache.org/repos/asf/hbase/repo
Commit: http://git-wip-us.apache.org/repos/asf/hbase/commit/f1d45359
Tree: http://git-wip-us.apache.org/repos/asf/hbase/tree/f1d45359
Diff: http://git-wip-us.apache.org/repos/asf/hbase/diff/f1d45359

Branch: refs/heads/master
Commit: f1d453599a944440b8a1075082e2dc7b7676416f
Parents: f48c92d
Author: tedyu <yu...@gmail.com>
Authored: Mon Mar 21 03:46:23 2016 -0700
Committer: tedyu <yu...@gmail.com>
Committed: Mon Mar 21 03:46:23 2016 -0700

----------------------------------------------------------------------
 .../hbase/master/snapshot/SnapshotManager.java  | 50 ++++++++++++++++++--
 .../hbase/namespace/NamespaceAuditor.java       | 15 ++++++
 .../hbase/namespace/NamespaceStateManager.java  |  2 +-
 .../hadoop/hbase/quotas/MasterQuotaManager.java | 10 ++++
 .../hbase/namespace/TestNamespaceAuditor.java   | 17 ++++---
 5 files changed, 79 insertions(+), 15 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/hbase/blob/f1d45359/hbase-server/src/main/java/org/apache/hadoop/hbase/master/snapshot/SnapshotManager.java
----------------------------------------------------------------------
diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/snapshot/SnapshotManager.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/snapshot/SnapshotManager.java
index d430493..db718ee 100644
--- a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/snapshot/SnapshotManager.java
+++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/snapshot/SnapshotManager.java
@@ -65,6 +65,7 @@ import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.NameStringPair;
 import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.ProcedureDescription;
 import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.SnapshotDescription;
 import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.SnapshotDescription.Type;
+import org.apache.hadoop.hbase.quotas.QuotaExceededException;
 import org.apache.hadoop.hbase.security.AccessDeniedException;
 import org.apache.hadoop.hbase.security.User;
 import org.apache.hadoop.hbase.snapshot.ClientSnapshotDescriptionUtils;
@@ -724,12 +725,41 @@ public class SnapshotManager extends MasterProcedureManager implements Stoppable
       if (cpHost != null) {
         cpHost.preRestoreSnapshot(reqSnapshot, snapshotTableDesc);
       }
+
+      int tableRegionCount = -1;
       try {
-        // Table already exist. Check and update the region quota for this table namespace
-        checkAndUpdateNamespaceRegionQuota(manifest, tableName);
+        // Table already exist. Check and update the region quota for this table namespace.
+        // The region quota may not be updated correctly if there are concurrent restore snapshot
+        // requests for the same table
+
+        tableRegionCount = getRegionCountOfTable(tableName);
+        int snapshotRegionCount = manifest.getRegionManifestsMap().size();
+
+        // Update region quota when snapshotRegionCount is larger. If we updated the region count
+        // to a smaller value before retoreSnapshot and the retoreSnapshot fails, we may fail to
+        // reset the region count to its original value if the region quota is consumed by other
+        // tables in the namespace
+        if (tableRegionCount > 0 && tableRegionCount < snapshotRegionCount) {
+          checkAndUpdateNamespaceRegionQuota(snapshotRegionCount, tableName);
+        }
         restoreSnapshot(snapshot, snapshotTableDesc);
+        // Update the region quota if snapshotRegionCount is smaller. This step should not fail
+        // because we have reserved enough region quota before hand
+        if (tableRegionCount > 0 && tableRegionCount > snapshotRegionCount) {
+          checkAndUpdateNamespaceRegionQuota(snapshotRegionCount, tableName);
+        }
+      } catch (QuotaExceededException e) {
+        LOG.error("Region quota exceeded while restoring the snapshot " + snapshot.getName()
+          + " as table " + tableName.getNameAsString(), e);
+        // If QEE is thrown before restoreSnapshot, quota information is not updated, so we
+        // should throw the exception directly. If QEE is thrown after restoreSnapshot, there
+        // must be unexpected reasons, we also throw the exception directly
+        throw e;
       } catch (IOException e) {
-        this.master.getMasterQuotaManager().removeTableFromNamespaceQuota(tableName);
+        if (tableRegionCount > 0) {
+          // reset the region count for table
+          checkAndUpdateNamespaceRegionQuota(tableRegionCount, tableName);
+        }
         LOG.error("Exception occurred while restoring the snapshot " + snapshot.getName()
             + " as table " + tableName.getNameAsString(), e);
         throw e;
@@ -769,12 +799,22 @@ public class SnapshotManager extends MasterProcedureManager implements Stoppable
     }
   }
 
-  private void checkAndUpdateNamespaceRegionQuota(SnapshotManifest manifest, TableName tableName)
+  private void checkAndUpdateNamespaceRegionQuota(int updatedRegionCount, TableName tableName)
       throws IOException {
     if (this.master.getMasterQuotaManager().isQuotaEnabled()) {
       this.master.getMasterQuotaManager().checkAndUpdateNamespaceRegionQuota(tableName,
-        manifest.getRegionManifestsMap().size());
+        updatedRegionCount);
+    }
+  }
+
+  /**
+   * @return cached region count, or -1 if quota manager is disabled or table status not found
+  */
+  private int getRegionCountOfTable(TableName tableName) throws IOException {
+    if (this.master.getMasterQuotaManager().isQuotaEnabled()) {
+      return this.master.getMasterQuotaManager().getRegionCountOfTable(tableName);
     }
+    return -1;
   }
 
   /**

http://git-wip-us.apache.org/repos/asf/hbase/blob/f1d45359/hbase-server/src/main/java/org/apache/hadoop/hbase/namespace/NamespaceAuditor.java
----------------------------------------------------------------------
diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/namespace/NamespaceAuditor.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/namespace/NamespaceAuditor.java
index 892ebb6..a30bfef 100644
--- a/hbase-server/src/main/java/org/apache/hadoop/hbase/namespace/NamespaceAuditor.java
+++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/namespace/NamespaceAuditor.java
@@ -100,6 +100,21 @@ public class NamespaceAuditor {
     }
   }
 
+  /**
+   * Get region count for table
+   * @param tName - table name
+   * @return cached region count, or -1 if table status not found
+   * @throws IOException Signals that the namespace auditor has not been initialized
+   */
+  public int getRegionCountOfTable(TableName tName) throws IOException {
+    if (stateManager.isInitialized()) {
+      NamespaceTableAndRegionInfo state = stateManager.getState(tName.getNamespaceAsString());
+      return state != null ? state.getRegionCountOfTable(tName) : -1;
+    }
+    checkTableTypeAndThrowException(tName);
+    return -1;
+  }
+
   public void checkQuotaToSplitRegion(HRegionInfo hri) throws IOException {
     if (!stateManager.isInitialized()) {
       throw new IOException(

http://git-wip-us.apache.org/repos/asf/hbase/blob/f1d45359/hbase-server/src/main/java/org/apache/hadoop/hbase/namespace/NamespaceStateManager.java
----------------------------------------------------------------------
diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/namespace/NamespaceStateManager.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/namespace/NamespaceStateManager.java
index 8035d32..523b056 100644
--- a/hbase-server/src/main/java/org/apache/hadoop/hbase/namespace/NamespaceStateManager.java
+++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/namespace/NamespaceStateManager.java
@@ -151,7 +151,7 @@ class NamespaceStateManager {
       currentStatus = getState(nspdesc.getName());
       if ((currentStatus.getTables().size()) >= TableNamespaceManager.getMaxTables(nspdesc)) {
         throw new QuotaExceededException("The table " + table.getNameAsString()
-            + "cannot be created as it would exceed maximum number of tables allowed "
+            + " cannot be created as it would exceed maximum number of tables allowed "
             + " in the namespace.  The total number of tables permitted is "
             + TableNamespaceManager.getMaxTables(nspdesc));
       }

http://git-wip-us.apache.org/repos/asf/hbase/blob/f1d45359/hbase-server/src/main/java/org/apache/hadoop/hbase/quotas/MasterQuotaManager.java
----------------------------------------------------------------------
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 caaea67..c48ab91 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
@@ -320,6 +320,16 @@ public class MasterQuotaManager implements RegionStateListener {
     }
   }
 
+  /**
+   * @return cached region count, or -1 if quota manager is disabled or table status not found
+  */
+  public int getRegionCountOfTable(TableName tName) throws IOException {
+    if (enabled) {
+      return namespaceQuotaManager.getRegionCountOfTable(tName);
+    }
+    return -1;
+  }
+
   public void onRegionMerged(HRegionInfo hri) throws IOException {
     if (enabled) {
       namespaceQuotaManager.updateQuotaForRegionMerge(hri);

http://git-wip-us.apache.org/repos/asf/hbase/blob/f1d45359/hbase-server/src/test/java/org/apache/hadoop/hbase/namespace/TestNamespaceAuditor.java
----------------------------------------------------------------------
diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/namespace/TestNamespaceAuditor.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/namespace/TestNamespaceAuditor.java
index 78fde21..40732f6 100644
--- a/hbase-server/src/test/java/org/apache/hadoop/hbase/namespace/TestNamespaceAuditor.java
+++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/namespace/TestNamespaceAuditor.java
@@ -827,16 +827,13 @@ public class TestNamespaceAuditor {
     assertEquals("Intial region count should be 4.", 4, nstate.getRegionCount());
 
     String snapshot = "snapshot_testRestoreSnapshotQuotaExceed";
+    // snapshot has 4 regions
     ADMIN.snapshot(snapshot, tableName1);
-
-    List<HRegionInfo> regions = ADMIN.getTableRegions(tableName1);
-    Collections.sort(regions);
-
-    ADMIN.split(tableName1, Bytes.toBytes("JJJ"));
-    Thread.sleep(2000);
-    assertEquals("Total regions count should be 5.", 5, nstate.getRegionCount());
-
-    ndesc.setConfiguration(TableNamespaceManager.KEY_MAX_REGIONS, "2");
+    // recreate table with 1 region and set max regions to 3 for namespace
+    ADMIN.disableTable(tableName1);
+    ADMIN.deleteTable(tableName1);
+    ADMIN.createTable(tableDescOne);
+    ndesc.setConfiguration(TableNamespaceManager.KEY_MAX_REGIONS, "3");
     ADMIN.modifyNamespace(ndesc);
 
     ADMIN.disableTable(tableName1);
@@ -845,7 +842,9 @@ public class TestNamespaceAuditor {
       fail("Region quota is exceeded so QuotaExceededException should be thrown but HBaseAdmin"
           + " wraps IOException into RestoreSnapshotException");
     } catch (RestoreSnapshotException ignore) {
+      assertTrue(ignore.getCause() instanceof QuotaExceededException);
     }
+    assertEquals(1, getNamespaceState(nsp).getRegionCount());
     ADMIN.enableTable(tableName1);
     ADMIN.deleteSnapshot(snapshot);
   }