From a3e7b2dc09739d325e62b3a2b05de23352e2141a Mon Sep 17 00:00:00 2001 From: Ashish Singhi Date: Fri, 24 Apr 2015 16:11:06 +0530 Subject: [PATCH] HBASE-13394 Failed to recreate a table when quota is enabled --- .../org/apache/hadoop/hbase/master/HMaster.java | 33 ++++++----- .../hbase/namespace/TestNamespaceAuditor.java | 64 ++++++++++++++++++++++ 2 files changed, 84 insertions(+), 13 deletions(-) diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/HMaster.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/HMaster.java index 9bd1dbb..e77f3c8 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/HMaster.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/HMaster.java @@ -1338,7 +1338,8 @@ public class HMaster extends HRegionServer implements MasterServices, Server { throw new MasterNotRunningException(); } - String namespace = hTableDescriptor.getTableName().getNamespaceAsString(); + TableName tableName = hTableDescriptor.getTableName(); + String namespace = tableName.getNamespaceAsString(); ensureNamespaceExists(namespace); HRegionInfo[] newRegions = ModifyRegionUtils.createHRegionInfos(hTableDescriptor, splitKeys); @@ -1346,19 +1347,25 @@ public class HMaster extends HRegionServer implements MasterServices, Server { sanityCheckTableDescriptor(hTableDescriptor); this.quotaManager.checkNamespaceTableAndRegionQuota(hTableDescriptor.getTableName(), newRegions.length); - if (cpHost != null) { - cpHost.preCreateTable(hTableDescriptor, newRegions); + Long procId = null; + try { + if (cpHost != null) { + cpHost.preCreateTable(hTableDescriptor, newRegions); + } + LOG.info(getClientIdAuditPrefix() + " create " + hTableDescriptor); + + // TODO: We can handle/merge duplicate requests, and differentiate the case of + // TableExistsException by saying if the schema is the same or not. + ProcedurePrepareLatch latch = ProcedurePrepareLatch.createLatch(); + procId = + this.procedureExecutor.submitProcedure(new CreateTableProcedure(procedureExecutor + .getEnvironment(), hTableDescriptor, newRegions, latch)); + latch.await(); + } catch (IOException e) { + this.quotaManager.removeTableFromNamespaceQuota(tableName); + LOG.error("Exception occurred while creating the table " + tableName.getNameAsString(), e); + throw e; } - LOG.info(getClientIdAuditPrefix() + " create " + hTableDescriptor); - - // TODO: We can handle/merge duplicate requests, and differentiate the case of - // TableExistsException by saying if the schema is the same or not. - ProcedurePrepareLatch latch = ProcedurePrepareLatch.createLatch(); - long procId = this.procedureExecutor.submitProcedure( - new CreateTableProcedure(procedureExecutor.getEnvironment(), - hTableDescriptor, newRegions, latch)); - latch.await(); - if (cpHost != null) { cpHost.postCreateTable(hTableDescriptor, newRegions); } 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 c86e0ff..3fee187 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 @@ -23,6 +23,7 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import java.io.IOException; import java.util.Collections; @@ -41,6 +42,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.HConstants; import org.apache.hadoop.hbase.HRegionInfo; import org.apache.hadoop.hbase.HTableDescriptor; import org.apache.hadoop.hbase.MiniHBaseCluster; @@ -100,6 +102,7 @@ public class TestNamespaceAuditor { UTIL.getConfiguration().set(CoprocessorHost.MASTER_COPROCESSOR_CONF_KEY, MasterSyncObserver.class.getName()); Configuration conf = UTIL.getConfiguration(); + conf.setInt(HConstants.HBASE_CLIENT_RETRIES_NUMBER, 5); conf.setBoolean(QuotaUtil.QUOTA_CONF_KEY, true); conf.setClass("hbase.coprocessor.regionserver.classes", CPRegionServerObserver.class, RegionServerObserver.class); @@ -470,6 +473,58 @@ public class TestNamespaceAuditor { htable.close(); } + /* + * Create a table and make sure that the table creation fails after adding this table entry into + * namespace quota cache. Now correct the failure and recreate the table with same name. + * HBASE-13394 + */ + @Test(timeout = 180000) + public void testRecreateTableWithSameNameAfterFirstTimeFailure() throws Exception { + String nsp1 = prefix + "_testRecreateTable"; + NamespaceDescriptor nspDesc = + NamespaceDescriptor.create(nsp1) + .addConfiguration(TableNamespaceManager.KEY_MAX_REGIONS, "20") + .addConfiguration(TableNamespaceManager.KEY_MAX_TABLES, "1").build(); + ADMIN.createNamespace(nspDesc); + final TableName tableOne = TableName.valueOf(nsp1 + TableName.NAMESPACE_DELIM + "table1"); + byte[] columnFamily = Bytes.toBytes("info"); + HTableDescriptor tableDescOne = new HTableDescriptor(tableOne); + tableDescOne.addFamily(new HColumnDescriptor(columnFamily)); + MasterSyncObserver.throwExceptionInPreCreateTable = true; + try { + try { + ADMIN.createTable(tableDescOne); + fail("Table " + tableOne.toString() + "creation should fail."); + } catch (Exception exp) { + LOG.error(exp); + } + assertFalse(ADMIN.tableExists(tableOne)); + + NamespaceTableAndRegionInfo nstate = getNamespaceState(nsp1); + assertEquals("First table creation failed in namespace so number of tables in namespace " + + "should be 0.", 0, nstate.getTables().size()); + + MasterSyncObserver.throwExceptionInPreCreateTable = false; + try { + ADMIN.createTable(tableDescOne); + } catch (Exception e) { + fail("Table " + tableOne.toString() + "creation should succeed."); + LOG.error(e); + } + assertTrue(ADMIN.tableExists(tableOne)); + nstate = getNamespaceState(nsp1); + assertEquals("First table was created successfully so table size in namespace should " + + "be one now.", 1, nstate.getTables().size()); + } finally { + MasterSyncObserver.throwExceptionInPreCreateTable = false; + if (ADMIN.tableExists(tableOne)) { + ADMIN.disableTable(tableOne); + deleteTable(tableOne); + } + ADMIN.deleteNamespace(nsp1); + } + } + private NamespaceTableAndRegionInfo getNamespaceState(String namespace) throws KeeperException, IOException { return getQuotaManager().getState(namespace); @@ -569,6 +624,7 @@ public class TestNamespaceAuditor { public static class MasterSyncObserver extends BaseMasterObserver { volatile CountDownLatch tableDeletionLatch; + static boolean throwExceptionInPreCreateTable; @Override public void preDeleteTable(ObserverContext ctx, @@ -582,6 +638,14 @@ public class TestNamespaceAuditor { throws IOException { tableDeletionLatch.countDown(); } + + @Override + public void preCreateTable(ObserverContext ctx, + HTableDescriptor desc, HRegionInfo[] regions) throws IOException { + if (throwExceptionInPreCreateTable) { + throw new IOException("Throw exception as it is demanded."); + } + } } private void deleteTable(final TableName tableName) throws Exception { -- 1.9.2.msysgit.0