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; *
  • DeleteTableAction
  • *
  • AddRowAction
  • * - * Actions performing DDL operations: + * Actions performing column family DDL operations: * + * Actions performing namespace DDL operations: + * *
    * * 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 namespaceMap = + new ConcurrentHashMap(); + ConcurrentHashMap enabledTables = new ConcurrentHashMap(); @@ -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 namespaceMap) { + // randomly select namespace from namespaceMap + if (namespaceMap.isEmpty()) { + return null; + } + // synchronization to prevent removal from multiple threads + synchronized (namespaceMap) { + ArrayList namespaceList = new ArrayList(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 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();