Skip to content

Commit

Permalink
HBASE-14212 Add IT test for procedure-v2-based namespace DDL (Stephen…
Browse files Browse the repository at this point in the history
… Yuan Jiang)
  • Loading branch information
syuanjiang committed Sep 25, 2015
1 parent eeefc3d commit daccb1c
Showing 1 changed file with 188 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,19 @@
* <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
Expand Down Expand Up @@ -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>();

Expand All @@ -140,6 +149,11 @@ public void cleanUpCluster() throws Exception {
admin.disableTables("ittable-\\d+");
admin.deleteTables("ittable-\\d+");
}
enabledTables.clear();
disabledTables.clear();
deletedTables.clear();
namespaceMap.clear();

Connection connection = getConnection();
connection.close();
super.cleanUpCluster();
Expand All @@ -165,6 +179,23 @@ protected synchronized Connection getConnection(){
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();
Expand Down Expand Up @@ -201,6 +232,148 @@ private abstract class MasterAction{
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)
Expand Down Expand Up @@ -610,6 +783,9 @@ void perform() throws IOException {
}

private enum ACTION {
CREATE_NAMESPACE,
MODIFY_NAMESPACE,
DELETE_NAMESPACE,
CREATE_TABLE,
DISABLE_TABLE,
ENABLE_TABLE,
Expand Down Expand Up @@ -637,6 +813,15 @@ public void run() {

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
Expand Down Expand Up @@ -741,6 +926,8 @@ private int runTest() throws Exception {
// verify
LOG.info("Verify actions of all threads succeeded");
checkException(workers);
LOG.info("Verify namespaces");
verifyNamespaces();
LOG.info("Verify states of all tables");
verifyTables();

Expand Down

0 comments on commit daccb1c

Please sign in to comment.