diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/AssignmentManager.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/AssignmentManager.java index 5bc1f2d..5e6c21e 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/AssignmentManager.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/AssignmentManager.java @@ -2802,7 +2802,12 @@ public class AssignmentManager { errorMsg = onRegionMergePONR(current, hri, serverName, transition); break; case MERGED: - errorMsg = onRegionMerged(current, hri, serverName, transition); + try { + regionStateListener.onRegionMerged(hri); + errorMsg = onRegionMerged(current, hri, serverName, transition); + } catch (IOException exp) { + errorMsg = StringUtils.stringifyException(exp); + } break; case MERGE_REVERTED: errorMsg = onRegionMergeReverted(current, hri, serverName, transition); 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 4496bdc..726b040 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 @@ -108,7 +108,18 @@ public class NamespaceAuditor { throw new IOException( "Split operation is being performed even before namespace auditor is initialized."); } else if (!stateManager - .checkAndUpdateNamespaceRegionCount(hri.getTable(), hri.getRegionName())) { + .checkAndUpdateNamespaceRegionCount(hri.getTable(), hri.getRegionName(), 1)) { + throw new QuotaExceededException("Region split not possible for :" + hri.getEncodedName() + + " as quota limits are exceeded "); + } + } + + public void updateQuotaForRegionMerge(HRegionInfo hri) throws IOException { + if (!stateManager.isInitialized()) { + throw new IOException( + "Merge operation is being performed even before namespace auditor is initialized."); + } else if (!stateManager + .checkAndUpdateNamespaceRegionCount(hri.getTable(), hri.getRegionName(), -1)) { throw new QuotaExceededException("Region split not possible for :" + hri.getEncodedName() + " as quota limits are exceeded "); } 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 144e2bb..c34a123 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 @@ -79,17 +79,19 @@ class NamespaceStateManager { * * @param TableName * @param regionName + * @param incr * @return true, if region can be added to table. * @throws IOException Signals that an I/O exception has occurred. */ synchronized boolean checkAndUpdateNamespaceRegionCount(TableName name, - byte[] regionName) throws IOException { + byte[] regionName, int incr) throws IOException { String namespace = name.getNamespaceAsString(); NamespaceDescriptor nspdesc = getNamespaceDescriptor(namespace); if (nspdesc != null) { NamespaceTableAndRegionInfo currentStatus; currentStatus = getState(namespace); - if (currentStatus.getRegionCount() >= TableNamespaceManager.getMaxRegions(nspdesc)) { + if (incr > 0 && + currentStatus.getRegionCount() >= TableNamespaceManager.getMaxRegions(nspdesc)) { LOG.warn("The region " + Bytes.toStringBinary(regionName) + " cannot be created. The region count will exceed quota on the namespace. " + "This may be transient, please retry later if there are any ongoing split" @@ -98,7 +100,7 @@ class NamespaceStateManager { } NamespaceTableAndRegionInfo nsInfo = nsStateCache.get(namespace); if (nsInfo != null) { - nsInfo.incRegionCountForTable(name, 1); + nsInfo.incRegionCountForTable(name, incr); } else { LOG.warn("Namespace state found null for namespace : " + namespace); } 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 8aba761..9893fc8 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 @@ -313,6 +313,12 @@ public class MasterQuotaManager implements RegionStateListener { } } + public void onRegionMerged(HRegionInfo hri) throws IOException { + if (enabled) { + namespaceQuotaManager.updateQuotaForRegionMerge(hri); + } + } + public void onRegionSplit(HRegionInfo hri) throws IOException { if (enabled) { namespaceQuotaManager.checkQuotaToSplitRegion(hri); diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/quotas/RegionStateListener.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/quotas/RegionStateListener.java index ee31a4d..6b954ac 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/quotas/RegionStateListener.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/quotas/RegionStateListener.java @@ -44,4 +44,11 @@ public interface RegionStateListener { */ void onRegionSplitReverted(HRegionInfo hri) throws IOException; + /** + * Process region merge event. + * + * @param hri An instance of HRegionInfo + * @throws IOException + */ + void onRegionMerged(HRegionInfo hri) throws IOException; } 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 d833ee8..b573130 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 @@ -37,6 +37,7 @@ import org.apache.hadoop.hbase.CoprocessorEnvironment; import org.apache.hadoop.hbase.DoNotRetryIOException; import org.apache.hadoop.hbase.HBaseTestingUtility; import org.apache.hadoop.hbase.HColumnDescriptor; +import org.apache.hadoop.hbase.HRegionInfo; import org.apache.hadoop.hbase.HTableDescriptor; import org.apache.hadoop.hbase.NamespaceDescriptor; import org.apache.hadoop.hbase.TableName; @@ -243,6 +244,44 @@ public class TestNamespaceAuditor { } @Test + public void testRegionMerge() throws Exception { + String nsp1 = prefix + "_regiontest"; + NamespaceDescriptor nspDesc = NamespaceDescriptor.create(nsp1) + .addConfiguration(TableNamespaceManager.KEY_MAX_REGIONS, "3") + .addConfiguration(TableNamespaceManager.KEY_MAX_TABLES, "2").build(); + admin.createNamespace(nspDesc); + final TableName tableTwo = TableName.valueOf(nsp1 + TableName.NAMESPACE_DELIM + "table2"); + byte[] columnFamily = Bytes.toBytes("info"); + HTableDescriptor tableDescOne = new HTableDescriptor(tableTwo); + tableDescOne.addFamily(new HColumnDescriptor(columnFamily)); + NamespaceTableAndRegionInfo stateInfo; + final int initialRegions = 3; + admin.createTable(tableDescOne, Bytes.toBytes("1"), Bytes.toBytes("1000"), initialRegions); + Connection connection = ConnectionFactory.createConnection(UTIL.getConfiguration()); + HTable htable = (HTable)connection.getTable(tableTwo); + UTIL.loadNumericRows(htable, Bytes.toBytes("info"), 1, 1000); + admin.flush(tableTwo); + stateInfo = getNamespaceState(nsp1); + List hris = admin.getTableRegions(tableTwo); + // merge the two regions + admin.mergeRegions(hris.get(0).getEncodedNameAsBytes(), + hris.get(1).getEncodedNameAsBytes(), false); + while (admin.getTableRegions(tableTwo).size() == initialRegions) { + Thread.sleep(100); + } + hris = admin.getTableRegions(tableTwo); + assertEquals(initialRegions-1, hris.size()); + + HRegion actualRegion = UTIL.getHBaseCluster().getRegions(tableTwo).get(1); + admin.split(tableTwo, getSplitKey(actualRegion.getStartKey(), actualRegion.getEndKey())); + while (admin.getTableRegions(tableTwo).size() != initialRegions) { + Thread.sleep(100); + } + assertEquals(initialRegions, admin.getTableRegions(tableTwo).size()); + htable.close(); + } + + @Test public void testRegionOperations() throws Exception { String nsp1 = prefix + "_regiontest"; NamespaceDescriptor nspDesc = NamespaceDescriptor.create(nsp1) @@ -289,8 +328,10 @@ public class TestNamespaceAuditor { admin.split(tableOne, getSplitKey(actualRegion.getStartKey(), actualRegion.getEndKey())); observer.postSplit.await(); // Make sure no regions have been added. - assertEquals(2, admin.getTableRegions(tableOne).size()); + List hris = admin.getTableRegions(tableOne); + assertEquals(2, hris.size()); assertTrue("split completed", observer.preSplitBeforePONR.getCount() == 1); + htable.close(); } @@ -313,6 +354,7 @@ public class TestNamespaceAuditor { public static class CustomObserver extends BaseRegionObserver{ volatile CountDownLatch postSplit; volatile CountDownLatch preSplitBeforePONR; + @Override public void postCompleteSplit(ObserverContext ctx) throws IOException {