Repository: hbase Updated Branches: refs/heads/branch-1 1c5551d9f -> 286edd8b5
HBASE-14212 Add IT test for procedure-v2-based namespace DDL (Stephen Yuan Jiang) Project: http://git-wip-us.apache.org/repos/asf/hbase/repo Commit: http://git-wip-us.apache.org/repos/asf/hbase/commit/286edd8b Tree: http://git-wip-us.apache.org/repos/asf/hbase/tree/286edd8b Diff: http://git-wip-us.apache.org/repos/asf/hbase/diff/286edd8b Branch: refs/heads/branch-1 Commit: 286edd8b55e145981b39f69e00f42c7141845962 Parents: 1c5551d Author: Stephen Yuan Jiang <syuanjiang...@gmail.com> Authored: Fri Sep 25 16:23:48 2015 -0700 Committer: Stephen Yuan Jiang <syuanjiang...@gmail.com> Committed: Fri Sep 25 16:41:55 2015 -0700 ---------------------------------------------------------------------- .../hbase/IntegrationTestDDLMasterFailover.java | 189 ++++++++++++++++++- 1 file changed, 188 insertions(+), 1 deletion(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/hbase/blob/286edd8b/hbase-it/src/test/java/org/apache/hadoop/hbase/IntegrationTestDDLMasterFailover.java ---------------------------------------------------------------------- diff --git a/hbase-it/src/test/java/org/apache/hadoop/hbase/IntegrationTestDDLMasterFailover.java b/hbase-it/src/test/java/org/apache/hadoop/hbase/IntegrationTestDDLMasterFailover.java index 75910c6..3ba7818 100644 --- a/hbase-it/src/test/java/org/apache/hadoop/hbase/IntegrationTestDDLMasterFailover.java +++ b/hbase-it/src/test/java/org/apache/hadoop/hbase/IntegrationTestDDLMasterFailover.java @@ -58,13 +58,19 @@ import org.junit.experimental.categories.Category; * <li>DeleteTableAction</li> * <li>AddRowAction</li> * </ul> - * Actions performing DDL operations: + * Actions performing column family DDL operations: * <ul> * <li>AddColumnFamilyAction</li> * <li>AlterColumnFamilyVersionsAction</li> * <li>AlterColumnFamilyEncodingAction</li> * <li>DeleteColumnFamilyAction</li> * </ul> + * Actions performing namespace DDL operations: + * <ul> + * <li>AddNamespaceAction</li> + * <li>AlterNamespaceAction</li> + * <li>DeleteNamespaceAction</li> + * </ul> * <br/> * * The threads run for a period of time (default 20 minutes) then are stopped at the end of @@ -115,6 +121,9 @@ public class IntegrationTestDDLMasterFailover extends IntegrationTestBase { protected int numThreads, numRegions; + ConcurrentHashMap<String, NamespaceDescriptor> namespaceMap = + new ConcurrentHashMap<String, NamespaceDescriptor>(); + ConcurrentHashMap<TableName, HTableDescriptor> enabledTables = new ConcurrentHashMap<TableName, HTableDescriptor>(); @@ -140,6 +149,11 @@ public class IntegrationTestDDLMasterFailover extends IntegrationTestBase { admin.disableTables("ittable-\\d+"); admin.deleteTables("ittable-\\d+"); } + enabledTables.clear(); + disabledTables.clear(); + deletedTables.clear(); + namespaceMap.clear(); + Connection connection = getConnection(); connection.close(); super.cleanUpCluster(); @@ -165,6 +179,23 @@ public class IntegrationTestDDLMasterFailover extends IntegrationTestBase { return connection; } + protected void verifyNamespaces() throws IOException{ + Connection connection = getConnection(); + Admin admin = connection.getAdmin(); + // iterating concurrent map + for (String nsName : namespaceMap.keySet()){ + try { + Assert.assertTrue( + "Namespace: " + nsName + " in namespaceMap does not exist", + admin.getNamespaceDescriptor(nsName) != null); + } catch (NamespaceNotFoundException nsnfe) { + Assert.fail( + "Namespace: " + nsName + " in namespaceMap does not exist: " + nsnfe.getMessage()); + } + } + admin.close(); + } + protected void verifyTables() throws IOException{ Connection connection = getConnection(); Admin admin = connection.getAdmin(); @@ -201,6 +232,148 @@ public class IntegrationTestDDLMasterFailover extends IntegrationTestBase { abstract void perform() throws IOException; } + private abstract class NamespaceAction extends MasterAction { + final String nsTestConfigKey = "hbase.namespace.testKey"; + + // NamespaceAction has implemented selectNamespace() shared by multiple namespace Actions + protected NamespaceDescriptor selectNamespace( + ConcurrentHashMap<String, NamespaceDescriptor> namespaceMap) { + // randomly select namespace from namespaceMap + if (namespaceMap.isEmpty()) { + return null; + } + // synchronization to prevent removal from multiple threads + synchronized (namespaceMap) { + ArrayList<String> namespaceList = new ArrayList<String>(namespaceMap.keySet()); + String randomKey = namespaceList.get(RandomUtils.nextInt(namespaceList.size())); + NamespaceDescriptor randomNsd = namespaceMap.get(randomKey); + // remove from namespaceMap + namespaceMap.remove(randomKey); + return randomNsd; + } + } + } + + private class CreateNamespaceAction extends NamespaceAction { + @Override + void perform() throws IOException { + Admin admin = connection.getAdmin(); + try { + NamespaceDescriptor nsd; + while (true) { + nsd = createNamespaceDesc(); + try { + if (admin.getNamespaceDescriptor(nsd.getName()) != null) { + // the namespace has already existed. + continue; + } else { + // currently, the code never return null - always throws exception if + // namespace is not found - this just a defensive programming to make + // sure null situation is handled in case the method changes in the + // future. + break; + } + } catch (NamespaceNotFoundException nsnfe) { + // This is expected for a random generated NamespaceDescriptor + break; + } + } + LOG.info("Creating namespace:" + nsd); + admin.createNamespace(nsd); + NamespaceDescriptor freshNamespaceDesc = admin.getNamespaceDescriptor(nsd.getName()); + Assert.assertTrue("Namespace: " + nsd + " was not created", freshNamespaceDesc != null); + LOG.info("Created namespace:" + freshNamespaceDesc); + namespaceMap.put(nsd.getName(), freshNamespaceDesc); + } catch (Exception e){ + LOG.warn("Caught exception in action: " + this.getClass()); + throw e; + } finally { + admin.close(); + } + verifyNamespaces(); + } + + private NamespaceDescriptor createNamespaceDesc() { + String namespaceName = "itnamespace" + String.format("%010d", + RandomUtils.nextInt(Integer.MAX_VALUE)); + NamespaceDescriptor nsd = NamespaceDescriptor.create(namespaceName).build(); + + nsd.setConfiguration( + nsTestConfigKey, + String.format("%010d", RandomUtils.nextInt(Integer.MAX_VALUE))); + return nsd; + } + } + + private class ModifyNamespaceAction extends NamespaceAction { + @Override + void perform() throws IOException { + NamespaceDescriptor selected = selectNamespace(namespaceMap); + if (selected == null) { + return; + } + + Admin admin = connection.getAdmin(); + try { + String namespaceName = selected.getName(); + LOG.info("Modifying namespace :" + selected); + NamespaceDescriptor modifiedNsd = NamespaceDescriptor.create(namespaceName).build(); + String nsValueNew; + do { + nsValueNew = String.format("%010d", RandomUtils.nextInt(Integer.MAX_VALUE)); + } while (selected.getConfigurationValue(nsTestConfigKey).equals(nsValueNew)); + modifiedNsd.setConfiguration(nsTestConfigKey, nsValueNew); + admin.modifyNamespace(modifiedNsd); + NamespaceDescriptor freshNamespaceDesc = admin.getNamespaceDescriptor(namespaceName); + Assert.assertTrue( + "Namespace: " + selected + " was not modified", + freshNamespaceDesc.getConfigurationValue(nsTestConfigKey).equals(nsValueNew)); + LOG.info("Modified namespace :" + freshNamespaceDesc); + namespaceMap.put(namespaceName, freshNamespaceDesc); + } catch (Exception e){ + LOG.warn("Caught exception in action: " + this.getClass()); + throw e; + } finally { + admin.close(); + } + verifyNamespaces(); + } + } + + private class DeleteNamespaceAction extends NamespaceAction { + @Override + void perform() throws IOException { + NamespaceDescriptor selected = selectNamespace(namespaceMap); + if (selected == null) { + return; + } + + Admin admin = connection.getAdmin(); + try { + String namespaceName = selected.getName(); + LOG.info("Deleting namespace :" + selected); + admin.deleteNamespace(namespaceName); + try { + if (admin.getNamespaceDescriptor(namespaceName) != null) { + // the namespace still exists. + Assert.assertTrue("Namespace: " + selected + " was not deleted", false); + } else { + LOG.info("Deleted namespace :" + selected); + } + } catch (NamespaceNotFoundException nsnfe) { + // This is expected result + LOG.info("Deleted namespace :" + selected); + } + } catch (Exception e){ + LOG.warn("Caught exception in action: " + this.getClass()); + throw e; + } finally { + admin.close(); + } + verifyNamespaces(); + } + } + private abstract class TableAction extends MasterAction{ // TableAction has implemented selectTable() shared by multiple table Actions protected HTableDescriptor selectTable(ConcurrentHashMap<TableName, HTableDescriptor> tableMap) @@ -610,6 +783,9 @@ public class IntegrationTestDDLMasterFailover extends IntegrationTestBase { } private enum ACTION { + CREATE_NAMESPACE, + MODIFY_NAMESPACE, + DELETE_NAMESPACE, CREATE_TABLE, DISABLE_TABLE, ENABLE_TABLE, @@ -637,6 +813,15 @@ public class IntegrationTestDDLMasterFailover extends IntegrationTestBase { try { switch (selectedAction) { + case CREATE_NAMESPACE: + new CreateNamespaceAction().perform(); + break; + case MODIFY_NAMESPACE: + new ModifyNamespaceAction().perform(); + break; + case DELETE_NAMESPACE: + new DeleteNamespaceAction().perform(); + break; case CREATE_TABLE: // stop creating new tables in the later stage of the test to avoid too many empty // tables @@ -741,6 +926,8 @@ public class IntegrationTestDDLMasterFailover extends IntegrationTestBase { // verify LOG.info("Verify actions of all threads succeeded"); checkException(workers); + LOG.info("Verify namespaces"); + verifyNamespaces(); LOG.info("Verify states of all tables"); verifyTables();