From 4db804a9fdba2520e61185ad8ae6524424a80033 Mon Sep 17 00:00:00 2001 From: Andrew Purtell Date: Wed, 3 Oct 2018 16:13:09 -0700 Subject: [PATCH] HBASE-21265 Split up TestRSGroups Signed-off-by: Ted Yu Conflicts: hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroups.java hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupsAdmin1.java hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupsBase.java --- ...tEnableRSGroup.java => TestEnableRSGroups.java} | 9 +- .../apache/hadoop/hbase/rsgroup/TestRSGroups.java | 467 --------- .../hadoop/hbase/rsgroup/TestRSGroupsAdmin1.java | 441 ++++++++ .../hadoop/hbase/rsgroup/TestRSGroupsAdmin2.java | 487 +++++++++ .../hadoop/hbase/rsgroup/TestRSGroupsBalance.java | 188 ++++ .../hadoop/hbase/rsgroup/TestRSGroupsBase.java | 1097 ++++---------------- .../hadoop/hbase/rsgroup/TestRSGroupsBasics.java | 320 ++++++ .../hadoop/hbase/rsgroup/TestRSGroupsKillRS.java | 149 +++ 8 files changed, 1804 insertions(+), 1354 deletions(-) rename hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/rsgroup/{TestEnableRSGroup.java => TestEnableRSGroups.java} (93%) delete mode 100644 hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroups.java create mode 100644 hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupsAdmin1.java create mode 100644 hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupsAdmin2.java create mode 100644 hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupsBalance.java create mode 100644 hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupsBasics.java create mode 100644 hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupsKillRS.java diff --git a/hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/rsgroup/TestEnableRSGroup.java b/hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/rsgroup/TestEnableRSGroups.java similarity index 93% rename from hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/rsgroup/TestEnableRSGroup.java rename to hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/rsgroup/TestEnableRSGroups.java index 77faec70f4..d15c7a89f8 100644 --- a/hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/rsgroup/TestEnableRSGroup.java +++ b/hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/rsgroup/TestEnableRSGroups.java @@ -40,13 +40,13 @@ import org.slf4j.LoggerFactory; * Test enable RSGroup */ @Category({ MediumTests.class }) -public class TestEnableRSGroup { +public class TestEnableRSGroups { @ClassRule public static final HBaseClassTestRule CLASS_RULE = - HBaseClassTestRule.forClass(TestEnableRSGroup.class); + HBaseClassTestRule.forClass(TestEnableRSGroups.class); - protected static final Logger LOG = LoggerFactory.getLogger(TestEnableRSGroup.class); + protected static final Logger LOG = LoggerFactory.getLogger(TestEnableRSGroups.class); private static final HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility(); @@ -71,7 +71,8 @@ public class TestEnableRSGroup { LOG.info("stopped master..."); final Configuration conf = TEST_UTIL.getMiniHBaseCluster().getConfiguration(); conf.set(CoprocessorHost.MASTER_COPROCESSOR_CONF_KEY, RSGroupAdminEndpoint.class.getName()); - conf.set(HConstants.HBASE_MASTER_LOADBALANCER_CLASS, RSGroupBasedLoadBalancer.class.getName()); + conf.set(HConstants.HBASE_MASTER_LOADBALANCER_CLASS, + RSGroupBasedLoadBalancer.class.getName()); TEST_UTIL.getMiniHBaseCluster().startMaster(); TEST_UTIL.getMiniHBaseCluster().waitForActiveAndReadyMaster(60000); diff --git a/hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroups.java b/hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroups.java deleted file mode 100644 index 5c2e64c55f..0000000000 --- a/hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroups.java +++ /dev/null @@ -1,467 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hadoop.hbase.rsgroup; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import java.io.IOException; -import java.util.Iterator; -import java.util.Optional; -import java.util.Set; - -import org.apache.hadoop.hbase.HBaseClassTestRule; -import org.apache.hadoop.hbase.HBaseTestingUtility; -import org.apache.hadoop.hbase.HColumnDescriptor; -import org.apache.hadoop.hbase.HConstants; -import org.apache.hadoop.hbase.HTableDescriptor; -import org.apache.hadoop.hbase.MiniHBaseCluster; -import org.apache.hadoop.hbase.NamespaceDescriptor; -import org.apache.hadoop.hbase.ServerName; -import org.apache.hadoop.hbase.TableName; -import org.apache.hadoop.hbase.Waiter; -import org.apache.hadoop.hbase.Waiter.Predicate; -import org.apache.hadoop.hbase.client.ClusterConnection; -import org.apache.hadoop.hbase.coprocessor.CoprocessorHost; -import org.apache.hadoop.hbase.coprocessor.MasterCoprocessor; -import org.apache.hadoop.hbase.coprocessor.MasterCoprocessorEnvironment; -import org.apache.hadoop.hbase.coprocessor.MasterObserver; -import org.apache.hadoop.hbase.coprocessor.ObserverContext; -import org.apache.hadoop.hbase.master.MasterCoprocessorHost; -import org.apache.hadoop.hbase.master.ServerManager; -import org.apache.hadoop.hbase.master.snapshot.SnapshotManager; -import org.apache.hadoop.hbase.net.Address; -import org.apache.hadoop.hbase.quotas.QuotaTableUtil; -import org.apache.hadoop.hbase.quotas.QuotaUtil; -import org.apache.hadoop.hbase.testclassification.MediumTests; -import org.apache.hadoop.hbase.util.Bytes; -import org.junit.After; -import org.junit.AfterClass; -import org.junit.Assert; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.ClassRule; -import org.junit.Test; -import org.junit.experimental.categories.Category; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import org.apache.hbase.thirdparty.com.google.common.collect.Sets; - -import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil; -import org.apache.hadoop.hbase.shaded.protobuf.generated.AdminProtos; - -@Category({MediumTests.class}) -public class TestRSGroups extends TestRSGroupsBase { - - @ClassRule - public static final HBaseClassTestRule CLASS_RULE = - HBaseClassTestRule.forClass(TestRSGroups.class); - - protected static final Logger LOG = LoggerFactory.getLogger(TestRSGroups.class); - private static boolean INIT = false; - private static RSGroupAdminEndpoint rsGroupAdminEndpoint; - private static CPMasterObserver observer; - - @BeforeClass - public static void setUp() throws Exception { - TEST_UTIL = new HBaseTestingUtility(); - TEST_UTIL.getConfiguration().setFloat( - "hbase.master.balancer.stochastic.tableSkewCost", 6000); - TEST_UTIL.getConfiguration().set( - HConstants.HBASE_MASTER_LOADBALANCER_CLASS, - RSGroupBasedLoadBalancer.class.getName()); - TEST_UTIL.getConfiguration().set(CoprocessorHost.MASTER_COPROCESSOR_CONF_KEY, - RSGroupAdminEndpoint.class.getName() + "," + CPMasterObserver.class.getName()); - TEST_UTIL.startMiniCluster(NUM_SLAVES_BASE - 1); - TEST_UTIL.getConfiguration().setInt( - ServerManager.WAIT_ON_REGIONSERVERS_MINTOSTART, - NUM_SLAVES_BASE - 1); - TEST_UTIL.getConfiguration().setBoolean(SnapshotManager.HBASE_SNAPSHOT_ENABLED, true); - - initialize(); - } - - private static void initialize() throws Exception { - admin = TEST_UTIL.getAdmin(); - cluster = TEST_UTIL.getHBaseCluster(); - master = ((MiniHBaseCluster)cluster).getMaster(); - - //wait for balancer to come online - TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate() { - @Override - public boolean evaluate() throws Exception { - return master.isInitialized() && - ((RSGroupBasedLoadBalancer) master.getLoadBalancer()).isOnline(); - } - }); - admin.setBalancerRunning(false,true); - rsGroupAdmin = new VerifyingRSGroupAdminClient( - new RSGroupAdminClient(TEST_UTIL.getConnection()), TEST_UTIL.getConfiguration()); - MasterCoprocessorHost host = master.getMasterCoprocessorHost(); - observer = (CPMasterObserver) host.findCoprocessor(CPMasterObserver.class.getName()); - rsGroupAdminEndpoint = (RSGroupAdminEndpoint) - host.findCoprocessor(RSGroupAdminEndpoint.class.getName()); - } - - @AfterClass - public static void tearDown() throws Exception { - TEST_UTIL.shutdownMiniCluster(); - } - - @Before - public void beforeMethod() throws Exception { - if (!INIT) { - INIT = true; - afterMethod(); - } - - } - - @After - public void afterMethod() throws Exception { - deleteTableIfNecessary(); - deleteNamespaceIfNecessary(); - deleteGroups(); - - for(ServerName sn : admin.listDecommissionedRegionServers()){ - admin.recommissionRegionServer(sn, null); - } - assertTrue(admin.listDecommissionedRegionServers().isEmpty()); - - int missing = NUM_SLAVES_BASE - getNumServers(); - LOG.info("Restoring servers: "+missing); - for(int i=0; i() { - @Override - public boolean evaluate() throws Exception { - LOG.info("Waiting for cleanup to finish " + rsGroupAdmin.listRSGroups()); - //Might be greater since moving servers back to default - //is after starting a server - - return rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP).getServers().size() - == NUM_SLAVES_BASE; - } - }); - } - - @Test - public void testBasicStartUp() throws IOException { - RSGroupInfo defaultInfo = rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP); - assertEquals(4, defaultInfo.getServers().size()); - // Assignment of root and meta regions. - int count = master.getAssignmentManager().getRegionStates().getRegionAssignments().size(); - //3 meta,namespace, group - assertEquals(3, count); - } - - @Test - public void testNamespaceCreateAndAssign() throws Exception { - LOG.info("testNamespaceCreateAndAssign"); - String nsName = tablePrefix+"_foo"; - final TableName tableName = TableName.valueOf(nsName, tablePrefix + "_testCreateAndAssign"); - RSGroupInfo appInfo = addGroup("appInfo", 1); - admin.createNamespace(NamespaceDescriptor.create(nsName) - .addConfiguration(RSGroupInfo.NAMESPACE_DESC_PROP_GROUP, "appInfo").build()); - final HTableDescriptor desc = new HTableDescriptor(tableName); - desc.addFamily(new HColumnDescriptor("f")); - admin.createTable(desc); - //wait for created table to be assigned - TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate() { - @Override - public boolean evaluate() throws Exception { - return getTableRegionMap().get(desc.getTableName()) != null; - } - }); - ServerName targetServer = - ServerName.parseServerName(appInfo.getServers().iterator().next().toString()); - AdminProtos.AdminService.BlockingInterface rs = - ((ClusterConnection) admin.getConnection()).getAdmin(targetServer); - //verify it was assigned to the right group - Assert.assertEquals(1, ProtobufUtil.getOnlineRegions(rs).size()); - } - - @Test - public void testDefaultNamespaceCreateAndAssign() throws Exception { - LOG.info("testDefaultNamespaceCreateAndAssign"); - String tableName = tablePrefix + "_testCreateAndAssign"; - admin.modifyNamespace(NamespaceDescriptor.create("default") - .addConfiguration(RSGroupInfo.NAMESPACE_DESC_PROP_GROUP, "default").build()); - final HTableDescriptor desc = new HTableDescriptor(TableName.valueOf(tableName)); - desc.addFamily(new HColumnDescriptor("f")); - admin.createTable(desc); - //wait for created table to be assigned - TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate() { - @Override - public boolean evaluate() throws Exception { - return getTableRegionMap().get(desc.getTableName()) != null; - } - }); - } - - @Test - public void testNamespaceConstraint() throws Exception { - String nsName = tablePrefix+"_foo"; - String groupName = tablePrefix+"_foo"; - LOG.info("testNamespaceConstraint"); - rsGroupAdmin.addRSGroup(groupName); - assertTrue(observer.preAddRSGroupCalled); - assertTrue(observer.postAddRSGroupCalled); - - admin.createNamespace(NamespaceDescriptor.create(nsName) - .addConfiguration(RSGroupInfo.NAMESPACE_DESC_PROP_GROUP, groupName) - .build()); - //test removing a referenced group - try { - rsGroupAdmin.removeRSGroup(groupName); - fail("Expected a constraint exception"); - } catch (IOException ex) { - } - //test modify group - //changing with the same name is fine - admin.modifyNamespace( - NamespaceDescriptor.create(nsName) - .addConfiguration(RSGroupInfo.NAMESPACE_DESC_PROP_GROUP, groupName) - .build()); - String anotherGroup = tablePrefix+"_anotherGroup"; - rsGroupAdmin.addRSGroup(anotherGroup); - //test add non-existent group - admin.deleteNamespace(nsName); - rsGroupAdmin.removeRSGroup(groupName); - assertTrue(observer.preRemoveRSGroupCalled); - assertTrue(observer.postRemoveRSGroupCalled); - try { - admin.createNamespace(NamespaceDescriptor.create(nsName) - .addConfiguration(RSGroupInfo.NAMESPACE_DESC_PROP_GROUP, "foo") - .build()); - fail("Expected a constraint exception"); - } catch (IOException ex) { - } - } - - @Test - public void testGroupInfoMultiAccessing() throws Exception { - RSGroupInfoManager manager = rsGroupAdminEndpoint.getGroupInfoManager(); - RSGroupInfo defaultGroup = manager.getRSGroup("default"); - // getRSGroup updates default group's server list - // this process must not affect other threads iterating the list - Iterator
it = defaultGroup.getServers().iterator(); - manager.getRSGroup("default"); - it.next(); - } - - public static class CPMasterObserver implements MasterCoprocessor, MasterObserver { - boolean preBalanceRSGroupCalled = false; - boolean postBalanceRSGroupCalled = false; - boolean preMoveServersCalled = false; - boolean postMoveServersCalled = false; - boolean preMoveTablesCalled = false; - boolean postMoveTablesCalled = false; - boolean preAddRSGroupCalled = false; - boolean postAddRSGroupCalled = false; - boolean preRemoveRSGroupCalled = false; - boolean postRemoveRSGroupCalled = false; - boolean preRemoveServersCalled = false; - boolean postRemoveServersCalled = false; - boolean preMoveServersAndTables = false; - boolean postMoveServersAndTables = false; - - @Override - public Optional getMasterObserver() { - return Optional.of(this); - } - @Override - public void preMoveServersAndTables(final ObserverContext ctx, - Set
servers, Set tables, String targetGroup) throws IOException { - preMoveServersAndTables = true; - } - @Override - public void postMoveServersAndTables(final ObserverContext ctx, - Set
servers, Set tables, String targetGroup) throws IOException { - postMoveServersAndTables = true; - } - @Override - public void preRemoveServers( - final ObserverContext ctx, - Set
servers) throws IOException { - preRemoveServersCalled = true; - } - @Override - public void postRemoveServers( - final ObserverContext ctx, - Set
servers) throws IOException { - postRemoveServersCalled = true; - } - @Override - public void preRemoveRSGroup(final ObserverContext ctx, - String name) throws IOException { - preRemoveRSGroupCalled = true; - } - @Override - public void postRemoveRSGroup(final ObserverContext ctx, - String name) throws IOException { - postRemoveRSGroupCalled = true; - } - @Override - public void preAddRSGroup(final ObserverContext ctx, - String name) throws IOException { - preAddRSGroupCalled = true; - } - @Override - public void postAddRSGroup(final ObserverContext ctx, - String name) throws IOException { - postAddRSGroupCalled = true; - } - @Override - public void preMoveTables(final ObserverContext ctx, - Set tables, String targetGroup) throws IOException { - preMoveTablesCalled = true; - } - @Override - public void postMoveTables(final ObserverContext ctx, - Set tables, String targetGroup) throws IOException { - postMoveTablesCalled = true; - } - @Override - public void preMoveServers(final ObserverContext ctx, - Set
servers, String targetGroup) throws IOException { - preMoveServersCalled = true; - } - - @Override - public void postMoveServers(final ObserverContext ctx, - Set
servers, String targetGroup) throws IOException { - postMoveServersCalled = true; - } - @Override - public void preBalanceRSGroup(final ObserverContext ctx, - String groupName) throws IOException { - preBalanceRSGroupCalled = true; - } - @Override - public void postBalanceRSGroup(final ObserverContext ctx, - String groupName, boolean balancerRan) throws IOException { - postBalanceRSGroupCalled = true; - } - } - @Test - public void testMoveServersAndTables() throws Exception { - super.testMoveServersAndTables(); - assertTrue(observer.preMoveServersAndTables); - assertTrue(observer.postMoveServersAndTables); - } - @Test - public void testTableMoveTruncateAndDrop() throws Exception { - super.testTableMoveTruncateAndDrop(); - assertTrue(observer.preMoveTablesCalled); - assertTrue(observer.postMoveTablesCalled); - } - - @Test - public void testRemoveServers() throws Exception { - super.testRemoveServers(); - assertTrue(observer.preRemoveServersCalled); - assertTrue(observer.postRemoveServersCalled); - } - - @Test - public void testMisplacedRegions() throws Exception { - final TableName tableName = TableName.valueOf(tablePrefix+"_testMisplacedRegions"); - LOG.info("testMisplacedRegions"); - - final RSGroupInfo RSGroupInfo = addGroup("testMisplacedRegions", 1); - - TEST_UTIL.createMultiRegionTable(tableName, new byte[]{'f'}, 15); - TEST_UTIL.waitUntilAllRegionsAssigned(tableName); - - rsGroupAdminEndpoint.getGroupInfoManager() - .moveTables(Sets.newHashSet(tableName), RSGroupInfo.getName()); - - admin.setBalancerRunning(true,true); - assertTrue(rsGroupAdmin.balanceRSGroup(RSGroupInfo.getName())); - admin.setBalancerRunning(false,true); - assertTrue(observer.preBalanceRSGroupCalled); - assertTrue(observer.postBalanceRSGroupCalled); - - TEST_UTIL.waitFor(60000, new Predicate() { - @Override - public boolean evaluate() throws Exception { - ServerName serverName = - ServerName.valueOf(RSGroupInfo.getServers().iterator().next().toString(), 1); - return admin.getConnection().getAdmin() - .getOnlineRegions(serverName).size() == 15; - } - }); - } - - @Test - public void testCloneSnapshot() throws Exception { - byte[] FAMILY = Bytes.toBytes("test"); - String snapshotName = tableName.getNameAsString() + "_snap"; - TableName clonedTableName = TableName.valueOf(tableName.getNameAsString() + "_clone"); - - // create base table - TEST_UTIL.createTable(tableName, FAMILY); - - // create snapshot - admin.snapshot(snapshotName, tableName); - - // clone - admin.cloneSnapshot(snapshotName, clonedTableName); - } - - @Test - public void testRSGroupsWithHBaseQuota() throws Exception { - TEST_UTIL.getConfiguration().setBoolean(QuotaUtil.QUOTA_CONF_KEY, true); - restartHBaseCluster(); - try { - TEST_UTIL.waitFor(90000, new Waiter.Predicate() { - @Override - public boolean evaluate() throws Exception { - return admin.isTableAvailable(QuotaTableUtil.QUOTA_TABLE_NAME); - } - }); - } finally { - TEST_UTIL.getConfiguration().setBoolean(QuotaUtil.QUOTA_CONF_KEY, false); - restartHBaseCluster(); - } - } - - private void restartHBaseCluster() throws Exception { - LOG.info("\n\nShutting down cluster"); - TEST_UTIL.shutdownMiniHBaseCluster(); - LOG.info("\n\nSleeping a bit"); - Thread.sleep(2000); - TEST_UTIL.restartHBaseCluster(NUM_SLAVES_BASE - 1); - initialize(); - } -} diff --git a/hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupsAdmin1.java b/hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupsAdmin1.java new file mode 100644 index 0000000000..2e3f2e911f --- /dev/null +++ b/hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupsAdmin1.java @@ -0,0 +1,441 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.hadoop.hbase.rsgroup; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +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.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.hadoop.hbase.HBaseClassTestRule; +import org.apache.hadoop.hbase.HColumnDescriptor; +import org.apache.hadoop.hbase.HTableDescriptor; +import org.apache.hadoop.hbase.NamespaceDescriptor; +import org.apache.hadoop.hbase.ServerName; +import org.apache.hadoop.hbase.TableName; +import org.apache.hadoop.hbase.TableNotFoundException; +import org.apache.hadoop.hbase.Waiter; +import org.apache.hadoop.hbase.constraint.ConstraintException; +import org.apache.hadoop.hbase.master.ServerManager; +import org.apache.hadoop.hbase.master.TableNamespaceManager; +import org.apache.hadoop.hbase.master.snapshot.SnapshotManager; +import org.apache.hadoop.hbase.net.Address; +import org.apache.hadoop.hbase.quotas.QuotaUtil; +import org.apache.hadoop.hbase.util.Bytes; + +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.ClassRule; +import org.junit.Test; +import org.junit.experimental.categories.Category; + +import org.apache.hbase.thirdparty.com.google.common.collect.Sets; +import org.apache.hadoop.hbase.testclassification.MediumTests; + +@Category({MediumTests.class}) +public class TestRSGroupsAdmin1 extends TestRSGroupsBase { + + @ClassRule + public static final HBaseClassTestRule CLASS_RULE = + HBaseClassTestRule.forClass(TestRSGroupsAdmin1.class); + + protected static final Log LOG = LogFactory.getLog(TestRSGroupsAdmin1.class); + + @BeforeClass + public static void setUp() throws Exception { + setUpTestBeforeClass(); + } + + @AfterClass + public static void tearDown() throws Exception { + tearDownAfterClass(); + } + + @Before + public void beforeMethod() throws Exception { + setUpBeforeMethod(); + } + + @After + public void afterMethod() throws Exception { + tearDownAfterMethod(); + } + + @Test + public void testValidGroupNames() throws IOException { + String[] badNames = {"foo*","foo@","-"}; + String[] goodNames = {"foo_123"}; + + for(String entry: badNames) { + try { + rsGroupAdmin.addRSGroup(entry); + fail("Expected a constraint exception for: "+entry); + } catch(ConstraintException ex) { + //expected + } + } + + for(String entry: goodNames) { + rsGroupAdmin.addRSGroup(entry); + } + } + + @Test + public void testBogusArgs() throws Exception { + assertNull(rsGroupAdmin.getRSGroupInfoOfTable(TableName.valueOf("nonexistent"))); + assertNull(rsGroupAdmin.getRSGroupOfServer(Address.fromParts("bogus",123))); + assertNull(rsGroupAdmin.getRSGroupInfo("bogus")); + + try { + rsGroupAdmin.removeRSGroup("bogus"); + fail("Expected removing bogus group to fail"); + } catch(ConstraintException ex) { + //expected + } + + try { + rsGroupAdmin.moveTables(Sets.newHashSet(TableName.valueOf("bogustable")), "bogus"); + fail("Expected move with bogus group to fail"); + } catch(ConstraintException|TableNotFoundException ex) { + //expected + } + + try { + rsGroupAdmin.moveServers(Sets.newHashSet(Address.fromParts("bogus",123)), "bogus"); + fail("Expected move with bogus group to fail"); + } catch(ConstraintException ex) { + //expected + } + + try { + admin.setBalancerRunning(true,true); + rsGroupAdmin.balanceRSGroup("bogus"); + admin.setBalancerRunning(false,true); + fail("Expected move with bogus group to fail"); + } catch(ConstraintException ex) { + //expected + } + } + + @Test + public void testNamespaceConstraint() throws Exception { + String nsName = tablePrefix+"_foo"; + String groupName = tablePrefix+"_foo"; + LOG.info("testNamespaceConstraint"); + rsGroupAdmin.addRSGroup(groupName); + assertTrue(observer.preAddRSGroupCalled); + assertTrue(observer.postAddRSGroupCalled); + + admin.createNamespace(NamespaceDescriptor.create(nsName) + .addConfiguration(RSGroupInfo.NAMESPACE_DESC_PROP_GROUP, groupName) + .build()); + //test removing a referenced group + try { + rsGroupAdmin.removeRSGroup(groupName); + fail("Expected a constraint exception"); + } catch (IOException ex) { + } + //test modify group + //changing with the same name is fine + admin.modifyNamespace( + NamespaceDescriptor.create(nsName) + .addConfiguration(RSGroupInfo.NAMESPACE_DESC_PROP_GROUP, groupName) + .build()); + String anotherGroup = tablePrefix+"_anotherGroup"; + rsGroupAdmin.addRSGroup(anotherGroup); + //test add non-existent group + admin.deleteNamespace(nsName); + rsGroupAdmin.removeRSGroup(groupName); + assertTrue(observer.preRemoveRSGroupCalled); + assertTrue(observer.postRemoveRSGroupCalled); + try { + admin.createNamespace(NamespaceDescriptor.create(nsName) + .addConfiguration(RSGroupInfo.NAMESPACE_DESC_PROP_GROUP, "foo") + .build()); + fail("Expected a constraint exception"); + } catch (IOException ex) { + } + } + + @Test + public void testGroupInfoMultiAccessing() throws Exception { + RSGroupInfoManager manager = rsGroupAdminEndpoint.getGroupInfoManager(); + RSGroupInfo defaultGroup = manager.getRSGroup("default"); + // getRSGroup updates default group's server list + // this process must not affect other threads iterating the list + Iterator
it = defaultGroup.getServers().iterator(); + manager.getRSGroup("default"); + it.next(); + } + + @Test + public void testFailRemoveGroup() throws IOException, InterruptedException { + int initNumGroups = rsGroupAdmin.listRSGroups().size(); + addGroup("bar", 3); + TEST_UTIL.createTable(tableName, Bytes.toBytes("f")); + rsGroupAdmin.moveTables(Sets.newHashSet(tableName), "bar"); + RSGroupInfo barGroup = rsGroupAdmin.getRSGroupInfo("bar"); + //group is not empty therefore it should fail + try { + rsGroupAdmin.removeRSGroup(barGroup.getName()); + fail("Expected remove group to fail"); + } catch(IOException e) { + } + //group cannot lose all it's servers therefore it should fail + try { + rsGroupAdmin.moveServers(barGroup.getServers(), RSGroupInfo.DEFAULT_GROUP); + fail("Expected move servers to fail"); + } catch(IOException e) { + } + + rsGroupAdmin.moveTables(barGroup.getTables(), RSGroupInfo.DEFAULT_GROUP); + try { + rsGroupAdmin.removeRSGroup(barGroup.getName()); + fail("Expected move servers to fail"); + } catch(IOException e) { + } + + rsGroupAdmin.moveServers(barGroup.getServers(), RSGroupInfo.DEFAULT_GROUP); + rsGroupAdmin.removeRSGroup(barGroup.getName()); + + Assert.assertEquals(initNumGroups, rsGroupAdmin.listRSGroups().size()); + } + + @Test + public void testMultiTableMove() throws Exception { + final TableName tableNameA = TableName.valueOf(tablePrefix + name.getMethodName() + "A"); + final TableName tableNameB = TableName.valueOf(tablePrefix + name.getMethodName() + "B"); + final byte[] familyNameBytes = Bytes.toBytes("f"); + String newGroupName = getGroupName(name.getMethodName()); + final RSGroupInfo newGroup = addGroup(newGroupName, 1); + + TEST_UTIL.createTable(tableNameA, familyNameBytes); + TEST_UTIL.createTable(tableNameB, familyNameBytes); + TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate() { + @Override + public boolean evaluate() throws Exception { + List regionsA = getTableRegionMap().get(tableNameA); + if (regionsA == null) { + return false; + } + List regionsB = getTableRegionMap().get(tableNameB); + if (regionsB == null) { + return false; + } + return getTableRegionMap().get(tableNameA).size() >= 1 + && getTableRegionMap().get(tableNameB).size() >= 1; + } + }); + + RSGroupInfo tableGrpA = rsGroupAdmin.getRSGroupInfoOfTable(tableNameA); + assertTrue(tableGrpA.getName().equals(RSGroupInfo.DEFAULT_GROUP)); + + RSGroupInfo tableGrpB = rsGroupAdmin.getRSGroupInfoOfTable(tableNameB); + assertTrue(tableGrpB.getName().equals(RSGroupInfo.DEFAULT_GROUP)); + //change table's group + LOG.info("Moving table [" + tableNameA + "," + tableNameB + "] to " + newGroup.getName()); + rsGroupAdmin.moveTables(Sets.newHashSet(tableNameA, tableNameB), newGroup.getName()); + + //verify group change + Assert.assertEquals(newGroup.getName(), + rsGroupAdmin.getRSGroupInfoOfTable(tableNameA).getName()); + + Assert.assertEquals(newGroup.getName(), + rsGroupAdmin.getRSGroupInfoOfTable(tableNameB).getName()); + + //verify tables' not exist in old group + Set DefaultTables = + rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP).getTables(); + assertFalse(DefaultTables.contains(tableNameA)); + assertFalse(DefaultTables.contains(tableNameB)); + + //verify tables' exist in new group + Set newGroupTables = rsGroupAdmin.getRSGroupInfo(newGroupName).getTables(); + assertTrue(newGroupTables.contains(tableNameA)); + assertTrue(newGroupTables.contains(tableNameB)); + } + + @Test + public void testTableMoveTruncateAndDrop() throws Exception { + final byte[] familyNameBytes = Bytes.toBytes("f"); + String newGroupName = getGroupName(name.getMethodName()); + final RSGroupInfo newGroup = addGroup(newGroupName, 2); + + TEST_UTIL.createMultiRegionTable(tableName, familyNameBytes, 5); + TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate() { + @Override + public boolean evaluate() throws Exception { + List regions = getTableRegionMap().get(tableName); + if (regions == null) { + return false; + } + + return getTableRegionMap().get(tableName).size() >= 5; + } + }); + + RSGroupInfo tableGrp = rsGroupAdmin.getRSGroupInfoOfTable(tableName); + assertTrue(tableGrp.getName().equals(RSGroupInfo.DEFAULT_GROUP)); + + //change table's group + LOG.info("Moving table "+tableName+" to "+newGroup.getName()); + rsGroupAdmin.moveTables(Sets.newHashSet(tableName), newGroup.getName()); + + //verify group change + Assert.assertEquals(newGroup.getName(), + rsGroupAdmin.getRSGroupInfoOfTable(tableName).getName()); + + TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate() { + @Override + public boolean evaluate() throws Exception { + Map> serverMap = getTableServerRegionMap().get(tableName); + int count = 0; + if (serverMap != null) { + for (ServerName rs : serverMap.keySet()) { + if (newGroup.containsServer(rs.getAddress())) { + count += serverMap.get(rs).size(); + } + } + } + return count == 5; + } + }); + + //test truncate + admin.disableTable(tableName); + admin.truncateTable(tableName, true); + Assert.assertEquals(1, rsGroupAdmin.getRSGroupInfo(newGroup.getName()).getTables().size()); + Assert.assertEquals(tableName, rsGroupAdmin.getRSGroupInfo( + newGroup.getName()).getTables().first()); + + //verify removed table is removed from group + TEST_UTIL.deleteTable(tableName); + Assert.assertEquals(0, rsGroupAdmin.getRSGroupInfo(newGroup.getName()).getTables().size()); + + assertTrue(observer.preMoveTablesCalled); + assertTrue(observer.postMoveTablesCalled); + } + + @Test + public void testDisabledTableMove() throws Exception { + final byte[] familyNameBytes = Bytes.toBytes("f"); + String newGroupName = getGroupName(name.getMethodName()); + final RSGroupInfo newGroup = addGroup(newGroupName, 2); + + TEST_UTIL.createMultiRegionTable(tableName, familyNameBytes, 5); + TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate() { + @Override + public boolean evaluate() throws Exception { + List regions = getTableRegionMap().get(tableName); + if (regions == null) { + return false; + } + return getTableRegionMap().get(tableName).size() >= 5; + } + }); + + RSGroupInfo tableGrp = rsGroupAdmin.getRSGroupInfoOfTable(tableName); + assertTrue(tableGrp.getName().equals(RSGroupInfo.DEFAULT_GROUP)); + + //test disable table + admin.disableTable(tableName); + + //change table's group + LOG.info("Moving table "+ tableName + " to " + newGroup.getName()); + rsGroupAdmin.moveTables(Sets.newHashSet(tableName), newGroup.getName()); + + //verify group change + Assert.assertEquals(newGroup.getName(), + rsGroupAdmin.getRSGroupInfoOfTable(tableName).getName()); + } + + @Test + public void testRSGroupListDoesNotContainFailedTableCreation() throws Exception { + toggleQuotaCheckAndRestartMiniCluster(true); + String nsp = "np1"; + NamespaceDescriptor nspDesc = + NamespaceDescriptor.create(nsp) + .addConfiguration(TableNamespaceManager.KEY_MAX_REGIONS, "5") + .addConfiguration(TableNamespaceManager.KEY_MAX_TABLES, "2").build(); + admin.createNamespace(nspDesc); + assertEquals(3, admin.listNamespaceDescriptors().length); + HColumnDescriptor fam1 = new HColumnDescriptor("fam1"); + HTableDescriptor tableDescOne = + new HTableDescriptor(TableName.valueOf(nsp + TableName.NAMESPACE_DELIM + "table1")); + tableDescOne.addFamily(fam1); + admin.createTable(tableDescOne); + + HTableDescriptor tableDescTwo = + new HTableDescriptor(TableName.valueOf(nsp + TableName.NAMESPACE_DELIM + "table2")); + tableDescTwo.addFamily(fam1); + boolean constraintViolated = false; + + try { + admin.createTable(tableDescTwo, Bytes.toBytes("AAA"), Bytes.toBytes("ZZZ"), + 6); + Assert.fail("Creation table should fail because of quota violation."); + } catch (Exception exp) { + assertTrue(exp instanceof IOException); + constraintViolated = true; + } finally { + assertTrue("Constraint not violated for table " + tableDescTwo.getTableName(), + constraintViolated); + } + List rsGroupInfoList = rsGroupAdmin.listRSGroups(); + boolean foundTable2 = false; + boolean foundTable1 = false; + for(int i = 0; i < rsGroupInfoList.size(); i++){ + if(rsGroupInfoList.get(i).getTables().contains(tableDescTwo.getTableName())){ + foundTable2 = true; + } + if(rsGroupInfoList.get(i).getTables().contains(tableDescOne.getTableName())){ + foundTable1 = true; + } + } + assertFalse("Found table2 in rsgroup list.", foundTable2); + assertTrue("Did not find table1 in rsgroup list", foundTable1); + + TEST_UTIL.deleteTable(tableDescOne.getTableName()); + admin.deleteNamespace(nspDesc.getName()); + toggleQuotaCheckAndRestartMiniCluster(false); + + } + + private void toggleQuotaCheckAndRestartMiniCluster(boolean enable) throws Exception{ + TEST_UTIL.shutdownMiniCluster(); + TEST_UTIL.getConfiguration().setBoolean(QuotaUtil.QUOTA_CONF_KEY, enable); + TEST_UTIL.startMiniCluster(NUM_SLAVES_BASE - 1); + TEST_UTIL.getConfiguration().setInt( + ServerManager.WAIT_ON_REGIONSERVERS_MINTOSTART, + NUM_SLAVES_BASE - 1); + TEST_UTIL.getConfiguration().setBoolean(SnapshotManager.HBASE_SNAPSHOT_ENABLED, true); + initialize(); + } +} diff --git a/hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupsAdmin2.java b/hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupsAdmin2.java new file mode 100644 index 0000000000..845528d39c --- /dev/null +++ b/hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupsAdmin2.java @@ -0,0 +1,487 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.hadoop.hbase.rsgroup; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.EnumSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.hadoop.hbase.ClusterMetrics.Option; +import org.apache.hadoop.hbase.HBaseClassTestRule; +import org.apache.hadoop.hbase.ServerName; +import org.apache.hadoop.hbase.TableName; +import org.apache.hadoop.hbase.Waiter; +import org.apache.hadoop.hbase.client.ClusterConnection; +import org.apache.hadoop.hbase.client.RegionInfo; +import org.apache.hadoop.hbase.constraint.ConstraintException; +import org.apache.hadoop.hbase.net.Address; +import org.apache.hadoop.hbase.util.Bytes; + +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.ClassRule; +import org.junit.Test; +import org.junit.experimental.categories.Category; + +import org.apache.hbase.thirdparty.com.google.common.collect.Sets; +import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil; +import org.apache.hadoop.hbase.shaded.protobuf.generated.AdminProtos; +import org.apache.hadoop.hbase.shaded.protobuf.generated.AdminProtos.GetServerInfoRequest; +import org.apache.hadoop.hbase.testclassification.MediumTests; + +@Category({MediumTests.class}) +public class TestRSGroupsAdmin2 extends TestRSGroupsBase { + + @ClassRule + public static final HBaseClassTestRule CLASS_RULE = + HBaseClassTestRule.forClass(TestRSGroupsAdmin2.class); + + protected static final Log LOG = LogFactory.getLog(TestRSGroupsAdmin2.class); + + @BeforeClass + public static void setUp() throws Exception { + setUpTestBeforeClass(); + } + + @AfterClass + public static void tearDown() throws Exception { + tearDownAfterClass(); + } + + @Before + public void beforeMethod() throws Exception { + setUpBeforeMethod(); + } + + @After + public void afterMethod() throws Exception { + tearDownAfterMethod(); + } + + @Test + public void testRegionMove() throws Exception { + final RSGroupInfo newGroup = addGroup(getGroupName(name.getMethodName()), 1); + final byte[] familyNameBytes = Bytes.toBytes("f"); + // All the regions created below will be assigned to the default group. + TEST_UTIL.createMultiRegionTable(tableName, familyNameBytes, 6); + TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate() { + @Override + public boolean evaluate() throws Exception { + List regions = getTableRegionMap().get(tableName); + if (regions == null) { + return false; + } + + return getTableRegionMap().get(tableName).size() >= 6; + } + }); + + //get target region to move + Map> assignMap = + getTableServerRegionMap().get(tableName); + String targetRegion = null; + for(ServerName server : assignMap.keySet()) { + targetRegion = assignMap.get(server).size() > 0 ? assignMap.get(server).get(0) : null; + if(targetRegion != null) { + break; + } + } + //get server which is not a member of new group + ServerName targetServer = null; + for (ServerName server : admin.getClusterMetrics(EnumSet.of(Option.LIVE_SERVERS)) + .getLiveServerMetrics().keySet()) { + if (!newGroup.containsServer(server.getAddress())) { + targetServer = server; + break; + } + } + + final AdminProtos.AdminService.BlockingInterface targetRS = + ((ClusterConnection) admin.getConnection()).getAdmin(targetServer); + + //move target server to group + rsGroupAdmin.moveServers(Sets.newHashSet(targetServer.getAddress()), + newGroup.getName()); + TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate() { + @Override + public boolean evaluate() throws Exception { + return ProtobufUtil.getOnlineRegions(targetRS).size() <= 0; + } + }); + + // Lets move this region to the new group. + TEST_UTIL.getAdmin().move(Bytes.toBytes(RegionInfo.encodeRegionName( + Bytes.toBytes(targetRegion))), Bytes.toBytes(targetServer.getServerName())); + TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate() { + @Override + public boolean evaluate() throws Exception { + return + getTableRegionMap().get(tableName) != null && + getTableRegionMap().get(tableName).size() == 6 && + admin.getClusterMetrics(EnumSet.of(Option.REGIONS_IN_TRANSITION)) + .getRegionStatesInTransition().size() < 1; + } + }); + + //verify that targetServer didn't open it + for (RegionInfo region: ProtobufUtil.getOnlineRegions(targetRS)) { + if (targetRegion.equals(region.getRegionNameAsString())) { + fail("Target server opened region"); + } + } + } + + @Test + public void testRegionServerMove() throws IOException, + InterruptedException { + int initNumGroups = rsGroupAdmin.listRSGroups().size(); + RSGroupInfo appInfo = addGroup(getGroupName(name.getMethodName()), 1); + RSGroupInfo adminInfo = addGroup(getGroupName(name.getMethodName()), 1); + RSGroupInfo dInfo = rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP); + Assert.assertEquals(initNumGroups + 2, rsGroupAdmin.listRSGroups().size()); + assertEquals(1, adminInfo.getServers().size()); + assertEquals(1, appInfo.getServers().size()); + assertEquals(getNumServers() - 2, dInfo.getServers().size()); + rsGroupAdmin.moveServers(appInfo.getServers(), + RSGroupInfo.DEFAULT_GROUP); + rsGroupAdmin.removeRSGroup(appInfo.getName()); + rsGroupAdmin.moveServers(adminInfo.getServers(), + RSGroupInfo.DEFAULT_GROUP); + rsGroupAdmin.removeRSGroup(adminInfo.getName()); + Assert.assertEquals(rsGroupAdmin.listRSGroups().size(), initNumGroups); + } + + @Test + public void testMoveServers() throws Exception { + //create groups and assign servers + addGroup("bar", 3); + rsGroupAdmin.addRSGroup("foo"); + + RSGroupInfo barGroup = rsGroupAdmin.getRSGroupInfo("bar"); + RSGroupInfo fooGroup = rsGroupAdmin.getRSGroupInfo("foo"); + assertEquals(3, barGroup.getServers().size()); + assertEquals(0, fooGroup.getServers().size()); + + //test fail bogus server move + try { + rsGroupAdmin.moveServers(Sets.newHashSet(Address.fromString("foo:9999")),"foo"); + fail("Bogus servers shouldn't have been successfully moved."); + } catch(IOException ex) { + String exp = "Source RSGroup for server foo:9999 does not exist."; + String msg = "Expected '"+exp+"' in exception message: "; + assertTrue(msg+" "+ex.getMessage(), ex.getMessage().contains(exp)); + } + + //test success case + LOG.info("moving servers "+barGroup.getServers()+" to group foo"); + rsGroupAdmin.moveServers(barGroup.getServers(), fooGroup.getName()); + + barGroup = rsGroupAdmin.getRSGroupInfo("bar"); + fooGroup = rsGroupAdmin.getRSGroupInfo("foo"); + assertEquals(0,barGroup.getServers().size()); + assertEquals(3,fooGroup.getServers().size()); + + LOG.info("moving servers "+fooGroup.getServers()+" to group default"); + rsGroupAdmin.moveServers(fooGroup.getServers(), RSGroupInfo.DEFAULT_GROUP); + + TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate() { + @Override + public boolean evaluate() throws Exception { + return getNumServers() == + rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP).getServers().size(); + } + }); + + fooGroup = rsGroupAdmin.getRSGroupInfo("foo"); + assertEquals(0,fooGroup.getServers().size()); + + //test group removal + LOG.info("Remove group "+barGroup.getName()); + rsGroupAdmin.removeRSGroup(barGroup.getName()); + Assert.assertEquals(null, rsGroupAdmin.getRSGroupInfo(barGroup.getName())); + LOG.info("Remove group "+fooGroup.getName()); + rsGroupAdmin.removeRSGroup(fooGroup.getName()); + Assert.assertEquals(null, rsGroupAdmin.getRSGroupInfo(fooGroup.getName())); + } + + @Test + public void testRemoveServers() throws Exception { + LOG.info("testRemoveServers"); + final RSGroupInfo newGroup = addGroup(getGroupName(name.getMethodName()), 3); + Iterator
iterator = newGroup.getServers().iterator(); + ServerName targetServer = ServerName.parseServerName(iterator.next().toString()); + + // remove online servers + try { + rsGroupAdmin.removeServers(Sets.newHashSet(targetServer.getAddress())); + fail("Online servers shouldn't have been successfully removed."); + } catch(IOException ex) { + String exp = "Server " + targetServer.getAddress() + + " is an online server, not allowed to remove."; + String msg = "Expected '" + exp + "' in exception message: "; + assertTrue(msg + " " + ex.getMessage(), ex.getMessage().contains(exp)); + } + assertTrue(newGroup.getServers().contains(targetServer.getAddress())); + + // remove dead servers + NUM_DEAD_SERVERS = cluster.getClusterMetrics().getDeadServerNames().size(); + AdminProtos.AdminService.BlockingInterface targetRS = + ((ClusterConnection) admin.getConnection()).getAdmin(targetServer); + try { + targetServer = ProtobufUtil.toServerName(targetRS.getServerInfo(null, + GetServerInfoRequest.newBuilder().build()).getServerInfo().getServerName()); + //stopping may cause an exception + //due to the connection loss + LOG.info("stopping server " + targetServer.getServerName()); + targetRS.stopServer(null, + AdminProtos.StopServerRequest.newBuilder().setReason("Die").build()); + NUM_DEAD_SERVERS ++; + } catch(Exception e) { + } + + //wait for stopped regionserver to dead server list + TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate() { + @Override + public boolean evaluate() throws Exception { + return !master.getServerManager().areDeadServersInProgress() + && cluster.getClusterMetrics().getDeadServerNames().size() == NUM_DEAD_SERVERS; + } + }); + + try { + rsGroupAdmin.removeServers(Sets.newHashSet(targetServer.getAddress())); + fail("Dead servers shouldn't have been successfully removed."); + } catch(IOException ex) { + String exp = "Server " + targetServer.getAddress() + " is on the dead servers list," + + " Maybe it will come back again, not allowed to remove."; + String msg = "Expected '" + exp + "' in exception message: "; + assertTrue(msg + " " + ex.getMessage(), ex.getMessage().contains(exp)); + } + assertTrue(newGroup.getServers().contains(targetServer.getAddress())); + + // remove decommissioned servers + List serversToDecommission = new ArrayList<>(); + targetServer = ServerName.parseServerName(iterator.next().toString()); + targetRS = ((ClusterConnection) admin.getConnection()).getAdmin(targetServer); + targetServer = ProtobufUtil.toServerName(targetRS.getServerInfo(null, + GetServerInfoRequest.newBuilder().build()).getServerInfo().getServerName()); + assertTrue(master.getServerManager().getOnlineServers().containsKey(targetServer)); + serversToDecommission.add(targetServer); + + admin.decommissionRegionServers(serversToDecommission, true); + assertEquals(1, admin.listDecommissionedRegionServers().size()); + + assertTrue(newGroup.getServers().contains(targetServer.getAddress())); + rsGroupAdmin.removeServers(Sets.newHashSet(targetServer.getAddress())); + Set
newGroupServers = rsGroupAdmin.getRSGroupInfo(newGroup.getName()).getServers(); + assertFalse(newGroupServers.contains(targetServer.getAddress())); + assertEquals(2, newGroupServers.size()); + + assertTrue(observer.preRemoveServersCalled); + assertTrue(observer.postRemoveServersCalled); + } + + @Test + public void testMoveServersAndTables() throws Exception { + LOG.info("testMoveServersAndTables"); + final RSGroupInfo newGroup = addGroup(getGroupName(name.getMethodName()), 1); + //create table + final byte[] familyNameBytes = Bytes.toBytes("f"); + TEST_UTIL.createMultiRegionTable(tableName, familyNameBytes, 5); + TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate() { + @Override + public boolean evaluate() throws Exception { + List regions = getTableRegionMap().get(tableName); + if (regions == null) { + return false; + } + + return getTableRegionMap().get(tableName).size() >= 5; + } + }); + + //get server which is not a member of new group + ServerName targetServer = null; + for(ServerName server : admin.getClusterMetrics(EnumSet.of(Option.LIVE_SERVERS)) + .getLiveServerMetrics().keySet()) { + if(!newGroup.containsServer(server.getAddress()) && + !rsGroupAdmin.getRSGroupInfo("master").containsServer(server.getAddress())) { + targetServer = server; + break; + } + } + + LOG.debug("Print group info : " + rsGroupAdmin.listRSGroups()); + int oldDefaultGroupServerSize = + rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP).getServers().size(); + int oldDefaultGroupTableSize = + rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP).getTables().size(); + + //test fail bogus server move + try { + rsGroupAdmin.moveServersAndTables(Sets.newHashSet(Address.fromString("foo:9999")), + Sets.newHashSet(tableName), newGroup.getName()); + fail("Bogus servers shouldn't have been successfully moved."); + } catch(IOException ex) { + String exp = "Source RSGroup for server foo:9999 does not exist."; + String msg = "Expected '" + exp + "' in exception message: "; + assertTrue(msg + " " + ex.getMessage(), ex.getMessage().contains(exp)); + } + + //test fail server move + try { + rsGroupAdmin.moveServersAndTables(Sets.newHashSet(targetServer.getAddress()), + Sets.newHashSet(tableName), RSGroupInfo.DEFAULT_GROUP); + fail("servers shouldn't have been successfully moved."); + } catch(IOException ex) { + String exp = "Target RSGroup " + RSGroupInfo.DEFAULT_GROUP + + " is same as source " + RSGroupInfo.DEFAULT_GROUP + " RSGroup."; + String msg = "Expected '" + exp + "' in exception message: "; + assertTrue(msg + " " + ex.getMessage(), ex.getMessage().contains(exp)); + } + + //verify default group info + Assert.assertEquals(oldDefaultGroupServerSize, + rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP).getServers().size()); + Assert.assertEquals(oldDefaultGroupTableSize, + rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP).getTables().size()); + + //verify new group info + Assert.assertEquals(1, + rsGroupAdmin.getRSGroupInfo(newGroup.getName()).getServers().size()); + Assert.assertEquals(0, + rsGroupAdmin.getRSGroupInfo(newGroup.getName()).getTables().size()); + + //get all region to move targetServer + List regionList = getTableRegionMap().get(tableName); + for(String region : regionList) { + // Lets move this region to the targetServer + TEST_UTIL.getAdmin().move(Bytes.toBytes(RegionInfo.encodeRegionName(Bytes.toBytes(region))), + Bytes.toBytes(targetServer.getServerName())); + } + + TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate() { + @Override + public boolean evaluate() throws Exception { + return getTableRegionMap().get(tableName) != null && + getTableRegionMap().get(tableName).size() == 5 && + getTableServerRegionMap().get(tableName).size() == 1 && + admin.getClusterMetrics(EnumSet.of(Option.REGIONS_IN_TRANSITION)) + .getRegionStatesInTransition().size() < 1; + } + }); + + //verify that all region move to targetServer + Assert.assertEquals(5, getTableServerRegionMap().get(tableName).get(targetServer).size()); + + //move targetServer and table to newGroup + LOG.info("moving server and table to newGroup"); + rsGroupAdmin.moveServersAndTables(Sets.newHashSet(targetServer.getAddress()), + Sets.newHashSet(tableName), newGroup.getName()); + + //verify group change + Assert.assertEquals(newGroup.getName(), + rsGroupAdmin.getRSGroupInfoOfTable(tableName).getName()); + + //verify servers' not exist in old group + Set
defaultServers = rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP) + .getServers(); + assertFalse(defaultServers.contains(targetServer.getAddress())); + + //verify servers' exist in new group + Set
newGroupServers = rsGroupAdmin.getRSGroupInfo(newGroup.getName()).getServers(); + assertTrue(newGroupServers.contains(targetServer.getAddress())); + + //verify tables' not exist in old group + Set defaultTables = rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP) + .getTables(); + assertFalse(defaultTables.contains(tableName)); + + //verify tables' exist in new group + Set newGroupTables = rsGroupAdmin.getRSGroupInfo(newGroup.getName()).getTables(); + assertTrue(newGroupTables.contains(tableName)); + + //verify that all region still assgin on targetServer + Assert.assertEquals(5, getTableServerRegionMap().get(tableName).get(targetServer).size()); + + assertTrue(observer.preMoveServersAndTables); + assertTrue(observer.postMoveServersAndTables); + } + + @Test + public void testMoveServersFromDefaultGroup() throws Exception { + //create groups and assign servers + rsGroupAdmin.addRSGroup("foo"); + + RSGroupInfo fooGroup = rsGroupAdmin.getRSGroupInfo("foo"); + assertEquals(0, fooGroup.getServers().size()); + RSGroupInfo defaultGroup = rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP); + + //test remove all servers from default + try { + rsGroupAdmin.moveServers(defaultGroup.getServers(), fooGroup.getName()); + fail(RSGroupAdminServer.KEEP_ONE_SERVER_IN_DEFAULT_ERROR_MESSAGE); + } catch (ConstraintException ex) { + assertTrue(ex.getMessage().contains(RSGroupAdminServer + .KEEP_ONE_SERVER_IN_DEFAULT_ERROR_MESSAGE)); + } + + //test success case, remove one server from default ,keep at least one server + if (defaultGroup.getServers().size() > 1) { + Address serverInDefaultGroup = defaultGroup.getServers().iterator().next(); + LOG.info("moving server " + serverInDefaultGroup + " from group default to group " + + fooGroup.getName()); + rsGroupAdmin.moveServers(Sets.newHashSet(serverInDefaultGroup), fooGroup.getName()); + } + + fooGroup = rsGroupAdmin.getRSGroupInfo("foo"); + LOG.info("moving servers " + fooGroup.getServers() + " to group default"); + rsGroupAdmin.moveServers(fooGroup.getServers(), RSGroupInfo.DEFAULT_GROUP); + + TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate() { + @Override + public boolean evaluate() throws Exception { + return getNumServers() == + rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP).getServers().size(); + } + }); + + fooGroup = rsGroupAdmin.getRSGroupInfo("foo"); + assertEquals(0, fooGroup.getServers().size()); + + //test group removal + LOG.info("Remove group " + fooGroup.getName()); + rsGroupAdmin.removeRSGroup(fooGroup.getName()); + Assert.assertEquals(null, rsGroupAdmin.getRSGroupInfo(fooGroup.getName())); + } + +} diff --git a/hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupsBalance.java b/hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupsBalance.java new file mode 100644 index 0000000000..b2d26c496b --- /dev/null +++ b/hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupsBalance.java @@ -0,0 +1,188 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.hadoop.hbase.rsgroup; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.util.List; +import java.util.Map; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.hadoop.hbase.HBaseClassTestRule; +import org.apache.hadoop.hbase.HColumnDescriptor; +import org.apache.hadoop.hbase.HTableDescriptor; +import org.apache.hadoop.hbase.NamespaceDescriptor; +import org.apache.hadoop.hbase.ServerName; +import org.apache.hadoop.hbase.TableName; +import org.apache.hadoop.hbase.Waiter; +import org.apache.hadoop.hbase.Waiter.Predicate; +import org.apache.hadoop.hbase.client.RegionInfo; +import org.apache.hadoop.hbase.util.Bytes; + +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.ClassRule; +import org.junit.Test; +import org.junit.experimental.categories.Category; + +import org.apache.hbase.thirdparty.com.google.common.collect.Sets; +import org.apache.hadoop.hbase.testclassification.MediumTests; + +@Category({MediumTests.class}) +public class TestRSGroupsBalance extends TestRSGroupsBase { + + @ClassRule + public static final HBaseClassTestRule CLASS_RULE = + HBaseClassTestRule.forClass(TestRSGroupsBalance.class); + + protected static final Log LOG = LogFactory.getLog(TestRSGroupsBalance.class); + + @BeforeClass + public static void setUp() throws Exception { + setUpTestBeforeClass(); + } + + @AfterClass + public static void tearDown() throws Exception { + tearDownAfterClass(); + } + + @Before + public void beforeMethod() throws Exception { + setUpBeforeMethod(); + } + + @After + public void afterMethod() throws Exception { + tearDownAfterMethod(); + } + + @Test + public void testGroupBalance() throws Exception { + LOG.info(name.getMethodName()); + String newGroupName = getGroupName(name.getMethodName()); + final RSGroupInfo newGroup = addGroup(newGroupName, 3); + + final TableName tableName = TableName.valueOf(tablePrefix+"_ns", name.getMethodName()); + admin.createNamespace( + NamespaceDescriptor.create(tableName.getNamespaceAsString()) + .addConfiguration(RSGroupInfo.NAMESPACE_DESC_PROP_GROUP, newGroupName).build()); + final byte[] familyNameBytes = Bytes.toBytes("f"); + final HTableDescriptor desc = new HTableDescriptor(tableName); + desc.addFamily(new HColumnDescriptor("f")); + byte [] startKey = Bytes.toBytes("aaaaa"); + byte [] endKey = Bytes.toBytes("zzzzz"); + admin.createTable(desc, startKey, endKey, 6); + TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate() { + @Override + public boolean evaluate() throws Exception { + List regions = getTableRegionMap().get(tableName); + if (regions == null) { + return false; + } + return regions.size() >= 6; + } + }); + + //make assignment uneven, move all regions to one server + Map> assignMap = + getTableServerRegionMap().get(tableName); + final ServerName first = assignMap.entrySet().iterator().next().getKey(); + for(RegionInfo region: admin.getTableRegions(tableName)) { + if(!assignMap.get(first).contains(region.getRegionNameAsString())) { + admin.move(region.getEncodedNameAsBytes(), Bytes.toBytes(first.getServerName())); + } + } + TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate() { + @Override + public boolean evaluate() throws Exception { + Map> map = getTableServerRegionMap().get(tableName); + if (map == null) { + return true; + } + List regions = map.get(first); + if (regions == null) { + return true; + } + return regions.size() >= 6; + } + }); + + //balance the other group and make sure it doesn't affect the new group + admin.setBalancerRunning(true,true); + rsGroupAdmin.balanceRSGroup(RSGroupInfo.DEFAULT_GROUP); + assertEquals(6, getTableServerRegionMap().get(tableName).get(first).size()); + + //disable balance, balancer will not be run and return false + admin.setBalancerRunning(false,true); + assertFalse(rsGroupAdmin.balanceRSGroup(newGroupName)); + assertEquals(6, getTableServerRegionMap().get(tableName).get(first).size()); + + //enable balance + admin.setBalancerRunning(true,true); + rsGroupAdmin.balanceRSGroup(newGroupName); + TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate() { + @Override + public boolean evaluate() throws Exception { + for (List regions : getTableServerRegionMap().get(tableName).values()) { + if (2 != regions.size()) { + return false; + } + } + return true; + } + }); + admin.setBalancerRunning(false,true); + } + + @Test + public void testMisplacedRegions() throws Exception { + final TableName tableName = TableName.valueOf(tablePrefix+"_testMisplacedRegions"); + LOG.info("testMisplacedRegions"); + + final RSGroupInfo RSGroupInfo = addGroup("testMisplacedRegions", 1); + + TEST_UTIL.createMultiRegionTable(tableName, new byte[]{'f'}, 15); + TEST_UTIL.waitUntilAllRegionsAssigned(tableName); + + rsGroupAdminEndpoint.getGroupInfoManager() + .moveTables(Sets.newHashSet(tableName), RSGroupInfo.getName()); + + admin.setBalancerRunning(true,true); + assertTrue(rsGroupAdmin.balanceRSGroup(RSGroupInfo.getName())); + admin.setBalancerRunning(false,true); + assertTrue(observer.preBalanceRSGroupCalled); + assertTrue(observer.postBalanceRSGroupCalled); + + TEST_UTIL.waitFor(60000, new Predicate() { + @Override + public boolean evaluate() throws Exception { + ServerName serverName = + ServerName.valueOf(RSGroupInfo.getServers().iterator().next().toString(), 1); + return admin.getConnection().getAdmin() + .getOnlineRegions(serverName).size() == 15; + } + }); + } + +} diff --git a/hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupsBase.java b/hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupsBase.java index e70a88b78f..74ccf74f2b 100644 --- a/hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupsBase.java +++ b/hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupsBase.java @@ -17,29 +17,25 @@ */ package org.apache.hadoop.hbase.rsgroup; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; import java.io.IOException; -import java.security.SecureRandom; -import java.util.ArrayList; import java.util.EnumSet; import java.util.HashSet; -import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.Optional; +import java.util.Random; import java.util.Set; import java.util.TreeMap; + import org.apache.hadoop.hbase.ClusterMetrics; import org.apache.hadoop.hbase.ClusterMetrics.Option; import org.apache.hadoop.hbase.HBaseCluster; import org.apache.hadoop.hbase.HBaseTestingUtility; -import org.apache.hadoop.hbase.HColumnDescriptor; -import org.apache.hadoop.hbase.HTableDescriptor; +import org.apache.hadoop.hbase.HConstants; +import org.apache.hadoop.hbase.MiniHBaseCluster; import org.apache.hadoop.hbase.NamespaceDescriptor; import org.apache.hadoop.hbase.RegionMetrics; import org.apache.hadoop.hbase.ServerMetrics; @@ -47,36 +43,35 @@ import org.apache.hadoop.hbase.ServerName; import org.apache.hadoop.hbase.TableName; import org.apache.hadoop.hbase.Waiter; import org.apache.hadoop.hbase.client.Admin; -import org.apache.hadoop.hbase.client.ClusterConnection; import org.apache.hadoop.hbase.client.RegionInfo; -import org.apache.hadoop.hbase.constraint.ConstraintException; +import org.apache.hadoop.hbase.client.TableDescriptor; +import org.apache.hadoop.hbase.coprocessor.CoprocessorHost; +import org.apache.hadoop.hbase.coprocessor.MasterCoprocessor; +import org.apache.hadoop.hbase.coprocessor.MasterCoprocessorEnvironment; +import org.apache.hadoop.hbase.coprocessor.MasterObserver; +import org.apache.hadoop.hbase.coprocessor.ObserverContext; import org.apache.hadoop.hbase.master.HMaster; +import org.apache.hadoop.hbase.master.MasterCoprocessorHost; +import org.apache.hadoop.hbase.master.ServerManager; +import org.apache.hadoop.hbase.master.snapshot.SnapshotManager; import org.apache.hadoop.hbase.net.Address; import org.apache.hadoop.hbase.util.Bytes; -import org.junit.Assert; -import org.junit.Before; + import org.junit.Rule; -import org.junit.Test; import org.junit.rules.TestName; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.apache.hbase.thirdparty.com.google.common.collect.Lists; import org.apache.hbase.thirdparty.com.google.common.collect.Maps; import org.apache.hbase.thirdparty.com.google.common.collect.Sets; -import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil; -import org.apache.hadoop.hbase.shaded.protobuf.generated.AdminProtos; -import org.apache.hadoop.hbase.shaded.protobuf.generated.AdminProtos.GetServerInfoRequest; public abstract class TestRSGroupsBase { protected static final Logger LOG = LoggerFactory.getLogger(TestRSGroupsBase.class); - @Rule - public TestName name = new TestName(); //shared protected final static String groupPrefix = "Group"; protected final static String tablePrefix = "Group"; - protected final static SecureRandom rand = new SecureRandom(); + protected final static Random rand = new Random(); //shared, cluster type specific protected static HBaseTestingUtility TEST_UTIL; @@ -84,26 +79,115 @@ public abstract class TestRSGroupsBase { protected static HBaseCluster cluster; protected static RSGroupAdmin rsGroupAdmin; protected static HMaster master; + protected static boolean INIT = false; + protected static RSGroupAdminEndpoint rsGroupAdminEndpoint; + protected static CPMasterObserver observer; public final static long WAIT_TIMEOUT = 60000*5; public final static int NUM_SLAVES_BASE = 4; //number of slaves for the smallest cluster public static int NUM_DEAD_SERVERS = 0; // Per test variables - TableName tableName; - @Before - public void setup() { + @Rule + public TestName name = new TestName(); + protected TableName tableName; + + public static void setUpTestBeforeClass() throws Exception { + TEST_UTIL = new HBaseTestingUtility(); + TEST_UTIL.getConfiguration().setFloat( + "hbase.master.balancer.stochastic.tableSkewCost", 6000); + TEST_UTIL.getConfiguration().set( + HConstants.HBASE_MASTER_LOADBALANCER_CLASS, + RSGroupBasedLoadBalancer.class.getName()); + TEST_UTIL.getConfiguration().set(CoprocessorHost.MASTER_COPROCESSOR_CONF_KEY, + RSGroupAdminEndpoint.class.getName() + "," + CPMasterObserver.class.getName()); + TEST_UTIL.startMiniCluster(NUM_SLAVES_BASE - 1); + TEST_UTIL.getConfiguration().setInt( + ServerManager.WAIT_ON_REGIONSERVERS_MINTOSTART, + NUM_SLAVES_BASE - 1); + TEST_UTIL.getConfiguration().setBoolean(SnapshotManager.HBASE_SNAPSHOT_ENABLED, true); + initialize(); + } + + protected static void initialize() throws Exception { + admin = TEST_UTIL.getAdmin(); + cluster = TEST_UTIL.getHBaseCluster(); + master = ((MiniHBaseCluster)cluster).getMaster(); + + //wait for balancer to come online + TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate() { + @Override + public boolean evaluate() throws Exception { + return master.isInitialized() && + ((RSGroupBasedLoadBalancer) master.getLoadBalancer()).isOnline(); + } + }); + admin.setBalancerRunning(false,true); + rsGroupAdmin = new VerifyingRSGroupAdminClient( + new RSGroupAdminClient(TEST_UTIL.getConnection()), TEST_UTIL.getConfiguration()); + MasterCoprocessorHost host = master.getMasterCoprocessorHost(); + observer = (CPMasterObserver) host.findCoprocessor(CPMasterObserver.class.getName()); + rsGroupAdminEndpoint = (RSGroupAdminEndpoint) + host.findCoprocessor(RSGroupAdminEndpoint.class.getName()); + } + + public static void tearDownAfterClass() throws Exception { + TEST_UTIL.shutdownMiniCluster(); + } + + public void setUpBeforeMethod() throws Exception { LOG.info(name.getMethodName()); tableName = TableName.valueOf(tablePrefix + "_" + name.getMethodName()); + if (!INIT) { + INIT = true; + tearDownAfterMethod(); + } + observer.resetFlags(); } - protected RSGroupInfo addGroup(String groupName, int serverCount) + public void tearDownAfterMethod() throws Exception { + deleteTableIfNecessary(); + deleteNamespaceIfNecessary(); + deleteGroups(); + + for(ServerName sn : admin.listDecommissionedRegionServers()){ + admin.recommissionRegionServer(sn, null); + } + assertTrue(admin.listDecommissionedRegionServers().isEmpty()); + + int missing = NUM_SLAVES_BASE - getNumServers(); + LOG.info("Restoring servers: "+missing); + for(int i=0; i() { + @Override + public boolean evaluate() throws Exception { + LOG.info("Waiting for cleanup to finish " + rsGroupAdmin.listRSGroups()); + //Might be greater since moving servers back to default + //is after starting a server + + return rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP).getServers().size() + == NUM_SLAVES_BASE; + } + }); + } + + public RSGroupInfo addGroup(String groupName, int serverCount) throws IOException, InterruptedException { RSGroupInfo defaultInfo = rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP); - assertTrue(defaultInfo != null); - assertTrue(defaultInfo.getServers().size() >= serverCount); rsGroupAdmin.addRSGroup(groupName); - Set
set = new HashSet<>(); for(Address server: defaultInfo.getServers()) { if(set.size() == serverCount) { @@ -113,24 +197,23 @@ public abstract class TestRSGroupsBase { } rsGroupAdmin.moveServers(set, groupName); RSGroupInfo result = rsGroupAdmin.getRSGroupInfo(groupName); - assertTrue(result.getServers().size() >= serverCount); return result; } - void removeGroup(String groupName) throws IOException { - RSGroupInfo RSGroupInfo = rsGroupAdmin.getRSGroupInfo(groupName); - rsGroupAdmin.moveTables(RSGroupInfo.getTables(), RSGroupInfo.DEFAULT_GROUP); - rsGroupAdmin.moveServers(RSGroupInfo.getServers(), RSGroupInfo.DEFAULT_GROUP); + public void removeGroup(String groupName) throws IOException { + RSGroupInfo groupInfo = rsGroupAdmin.getRSGroupInfo(groupName); + rsGroupAdmin.moveTables(groupInfo.getTables(), RSGroupInfo.DEFAULT_GROUP); + rsGroupAdmin.moveServers(groupInfo.getServers(), RSGroupInfo.DEFAULT_GROUP); rsGroupAdmin.removeRSGroup(groupName); } - protected void deleteTableIfNecessary() throws IOException { - for (HTableDescriptor desc : TEST_UTIL.getAdmin().listTables(tablePrefix+".*")) { + public void deleteTableIfNecessary() throws IOException { + for (TableDescriptor desc : TEST_UTIL.getAdmin().listTables(tablePrefix+".*")) { TEST_UTIL.deleteTable(desc.getTableName()); } } - protected void deleteNamespaceIfNecessary() throws IOException { + public void deleteNamespaceIfNecessary() throws IOException { for (NamespaceDescriptor desc : TEST_UTIL.getAdmin().listNamespaceDescriptors()) { if(desc.getName().startsWith(tablePrefix)) { admin.deleteNamespace(desc.getName()); @@ -138,9 +221,8 @@ public abstract class TestRSGroupsBase { } } - protected void deleteGroups() throws IOException { - RSGroupAdmin groupAdmin = - new RSGroupAdminClient(TEST_UTIL.getConnection()); + public void deleteGroups() throws IOException { + RSGroupAdmin groupAdmin = new RSGroupAdminClient(TEST_UTIL.getConnection()); for(RSGroupInfo group: groupAdmin.listRSGroups()) { if(!group.getName().equals(RSGroupInfo.DEFAULT_GROUP)) { groupAdmin.moveTables(group.getTables(), RSGroupInfo.DEFAULT_GROUP); @@ -192,85 +274,6 @@ public abstract class TestRSGroupsBase { return map; } - @Test - public void testBogusArgs() throws Exception { - assertNull(rsGroupAdmin.getRSGroupInfoOfTable(TableName.valueOf("nonexistent"))); - assertNull(rsGroupAdmin.getRSGroupOfServer(Address.fromParts("bogus",123))); - assertNull(rsGroupAdmin.getRSGroupInfo("bogus")); - - try { - rsGroupAdmin.removeRSGroup("bogus"); - fail("Expected removing bogus group to fail"); - } catch(ConstraintException ex) { - //expected - } - - try { - rsGroupAdmin.moveTables(Sets.newHashSet(TableName.valueOf("bogustable")), "bogus"); - fail("Expected move with bogus group to fail"); - } catch(ConstraintException ex) { - //expected - } - - try { - rsGroupAdmin.moveServers(Sets.newHashSet(Address.fromParts("bogus",123)), "bogus"); - fail("Expected move with bogus group to fail"); - } catch(ConstraintException ex) { - //expected - } - - try { - admin.setBalancerRunning(true,true); - rsGroupAdmin.balanceRSGroup("bogus"); - admin.setBalancerRunning(false,true); - fail("Expected move with bogus group to fail"); - } catch(ConstraintException ex) { - //expected - } - } - - @Test - public void testCreateMultiRegion() throws IOException { - byte[] end = {1,3,5,7,9}; - byte[] start = {0,2,4,6,8}; - byte[][] f = {Bytes.toBytes("f")}; - TEST_UTIL.createTable(tableName, f,1,start,end,10); - } - - @Test - public void testCreateAndDrop() throws Exception { - TEST_UTIL.createTable(tableName, Bytes.toBytes("cf")); - //wait for created table to be assigned - TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate() { - @Override - public boolean evaluate() throws Exception { - return getTableRegionMap().get(tableName) != null; - } - }); - TEST_UTIL.deleteTable(tableName); - } - - - @Test - public void testSimpleRegionServerMove() throws IOException, - InterruptedException { - int initNumGroups = rsGroupAdmin.listRSGroups().size(); - RSGroupInfo appInfo = addGroup(getGroupName(name.getMethodName()), 1); - RSGroupInfo adminInfo = addGroup(getGroupName(name.getMethodName()), 1); - RSGroupInfo dInfo = rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP); - Assert.assertEquals(initNumGroups + 2, rsGroupAdmin.listRSGroups().size()); - assertEquals(1, adminInfo.getServers().size()); - assertEquals(1, appInfo.getServers().size()); - assertEquals(getNumServers() - 2, dInfo.getServers().size()); - rsGroupAdmin.moveServers(appInfo.getServers(), - RSGroupInfo.DEFAULT_GROUP); - rsGroupAdmin.removeRSGroup(appInfo.getName()); - rsGroupAdmin.moveServers(adminInfo.getServers(), - RSGroupInfo.DEFAULT_GROUP); - rsGroupAdmin.removeRSGroup(adminInfo.getName()); - Assert.assertEquals(rsGroupAdmin.listRSGroups().size(), initNumGroups); - } - // return the real number of region servers, excluding the master embedded region server in 2.0+ public int getNumServers() throws IOException { ClusterMetrics status = @@ -285,806 +288,134 @@ public abstract class TestRSGroupsBase { return count; } - @Test - public void testMoveServers() throws Exception { - //create groups and assign servers - addGroup("bar", 3); - rsGroupAdmin.addRSGroup("foo"); - - RSGroupInfo barGroup = rsGroupAdmin.getRSGroupInfo("bar"); - RSGroupInfo fooGroup = rsGroupAdmin.getRSGroupInfo("foo"); - assertEquals(3, barGroup.getServers().size()); - assertEquals(0, fooGroup.getServers().size()); - - //test fail bogus server move - try { - rsGroupAdmin.moveServers(Sets.newHashSet(Address.fromString("foo:9999")),"foo"); - fail("Bogus servers shouldn't have been successfully moved."); - } catch(IOException ex) { - String exp = "Source RSGroup for server foo:9999 does not exist."; - String msg = "Expected '"+exp+"' in exception message: "; - assertTrue(msg+" "+ex.getMessage(), ex.getMessage().contains(exp)); - } - - //test success case - LOG.info("moving servers "+barGroup.getServers()+" to group foo"); - rsGroupAdmin.moveServers(barGroup.getServers(), fooGroup.getName()); - - barGroup = rsGroupAdmin.getRSGroupInfo("bar"); - fooGroup = rsGroupAdmin.getRSGroupInfo("foo"); - assertEquals(0,barGroup.getServers().size()); - assertEquals(3,fooGroup.getServers().size()); - - LOG.info("moving servers "+fooGroup.getServers()+" to group default"); - rsGroupAdmin.moveServers(fooGroup.getServers(), RSGroupInfo.DEFAULT_GROUP); - - TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate() { - @Override - public boolean evaluate() throws Exception { - return getNumServers() == - rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP).getServers().size(); - } - }); - - fooGroup = rsGroupAdmin.getRSGroupInfo("foo"); - assertEquals(0,fooGroup.getServers().size()); - - //test group removal - LOG.info("Remove group "+barGroup.getName()); - rsGroupAdmin.removeRSGroup(barGroup.getName()); - Assert.assertEquals(null, rsGroupAdmin.getRSGroupInfo(barGroup.getName())); - LOG.info("Remove group "+fooGroup.getName()); - rsGroupAdmin.removeRSGroup(fooGroup.getName()); - Assert.assertEquals(null, rsGroupAdmin.getRSGroupInfo(fooGroup.getName())); + public String getGroupName(String baseName) { + return groupPrefix+"_"+baseName+"_"+rand.nextInt(Integer.MAX_VALUE); } - @Test - public void testMoveServersFromDefaultGroup() throws Exception { - //create groups and assign servers - rsGroupAdmin.addRSGroup("foo"); - - RSGroupInfo fooGroup = rsGroupAdmin.getRSGroupInfo("foo"); - assertEquals(0, fooGroup.getServers().size()); - RSGroupInfo defaultGroup = rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP); - - //test remove all servers from default - try { - rsGroupAdmin.moveServers(defaultGroup.getServers(), fooGroup.getName()); - fail(RSGroupAdminServer.KEEP_ONE_SERVER_IN_DEFAULT_ERROR_MESSAGE); - } catch (ConstraintException ex) { - assertTrue(ex.getMessage().contains(RSGroupAdminServer - .KEEP_ONE_SERVER_IN_DEFAULT_ERROR_MESSAGE)); - } + public static class CPMasterObserver implements MasterCoprocessor, MasterObserver { + boolean preBalanceRSGroupCalled = false; + boolean postBalanceRSGroupCalled = false; + boolean preMoveServersCalled = false; + boolean postMoveServersCalled = false; + boolean preMoveTablesCalled = false; + boolean postMoveTablesCalled = false; + boolean preAddRSGroupCalled = false; + boolean postAddRSGroupCalled = false; + boolean preRemoveRSGroupCalled = false; + boolean postRemoveRSGroupCalled = false; + boolean preRemoveServersCalled = false; + boolean postRemoveServersCalled = false; + boolean preMoveServersAndTables = false; + boolean postMoveServersAndTables = false; - //test success case, remove one server from default ,keep at least one server - if (defaultGroup.getServers().size() > 1) { - Address serverInDefaultGroup = defaultGroup.getServers().iterator().next(); - LOG.info("moving server " + serverInDefaultGroup + " from group default to group " + - fooGroup.getName()); - rsGroupAdmin.moveServers(Sets.newHashSet(serverInDefaultGroup), fooGroup.getName()); + public void resetFlags() { + preBalanceRSGroupCalled = false; + postBalanceRSGroupCalled = false; + preMoveServersCalled = false; + postMoveServersCalled = false; + preMoveTablesCalled = false; + postMoveTablesCalled = false; + preAddRSGroupCalled = false; + postAddRSGroupCalled = false; + preRemoveRSGroupCalled = false; + postRemoveRSGroupCalled = false; + preRemoveServersCalled = false; + postRemoveServersCalled = false; + preMoveServersAndTables = false; + postMoveServersAndTables = false; } - fooGroup = rsGroupAdmin.getRSGroupInfo("foo"); - LOG.info("moving servers " + fooGroup.getServers() + " to group default"); - rsGroupAdmin.moveServers(fooGroup.getServers(), RSGroupInfo.DEFAULT_GROUP); - - TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate() { - @Override - public boolean evaluate() throws Exception { - return getNumServers() == - rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP).getServers().size(); - } - }); - - fooGroup = rsGroupAdmin.getRSGroupInfo("foo"); - assertEquals(0, fooGroup.getServers().size()); - - //test group removal - LOG.info("Remove group " + fooGroup.getName()); - rsGroupAdmin.removeRSGroup(fooGroup.getName()); - Assert.assertEquals(null, rsGroupAdmin.getRSGroupInfo(fooGroup.getName())); - } - - @Test - public void testTableMoveTruncateAndDrop() throws Exception { - final byte[] familyNameBytes = Bytes.toBytes("f"); - String newGroupName = getGroupName(name.getMethodName()); - final RSGroupInfo newGroup = addGroup(newGroupName, 2); - - TEST_UTIL.createMultiRegionTable(tableName, familyNameBytes, 5); - TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate() { - @Override - public boolean evaluate() throws Exception { - List regions = getTableRegionMap().get(tableName); - if (regions == null) { - return false; - } - - return getTableRegionMap().get(tableName).size() >= 5; - } - }); - - RSGroupInfo tableGrp = rsGroupAdmin.getRSGroupInfoOfTable(tableName); - assertTrue(tableGrp.getName().equals(RSGroupInfo.DEFAULT_GROUP)); - - //change table's group - LOG.info("Moving table "+tableName+" to "+newGroup.getName()); - rsGroupAdmin.moveTables(Sets.newHashSet(tableName), newGroup.getName()); - - //verify group change - Assert.assertEquals(newGroup.getName(), - rsGroupAdmin.getRSGroupInfoOfTable(tableName).getName()); - - TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate() { - @Override - public boolean evaluate() throws Exception { - Map> serverMap = getTableServerRegionMap().get(tableName); - int count = 0; - if (serverMap != null) { - for (ServerName rs : serverMap.keySet()) { - if (newGroup.containsServer(rs.getAddress())) { - count += serverMap.get(rs).size(); - } - } - } - return count == 5; - } - }); - - //test truncate - admin.disableTable(tableName); - admin.truncateTable(tableName, true); - Assert.assertEquals(1, rsGroupAdmin.getRSGroupInfo(newGroup.getName()).getTables().size()); - Assert.assertEquals(tableName, rsGroupAdmin.getRSGroupInfo( - newGroup.getName()).getTables().first()); - - //verify removed table is removed from group - TEST_UTIL.deleteTable(tableName); - Assert.assertEquals(0, rsGroupAdmin.getRSGroupInfo(newGroup.getName()).getTables().size()); - } - @Test - public void testGroupBalance() throws Exception { - LOG.info(name.getMethodName()); - String newGroupName = getGroupName(name.getMethodName()); - final RSGroupInfo newGroup = addGroup(newGroupName, 3); - - final TableName tableName = TableName.valueOf(tablePrefix+"_ns", name.getMethodName()); - admin.createNamespace( - NamespaceDescriptor.create(tableName.getNamespaceAsString()) - .addConfiguration(RSGroupInfo.NAMESPACE_DESC_PROP_GROUP, newGroupName).build()); - final byte[] familyNameBytes = Bytes.toBytes("f"); - final HTableDescriptor desc = new HTableDescriptor(tableName); - desc.addFamily(new HColumnDescriptor("f")); - byte [] startKey = Bytes.toBytes("aaaaa"); - byte [] endKey = Bytes.toBytes("zzzzz"); - admin.createTable(desc, startKey, endKey, 6); - TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate() { - @Override - public boolean evaluate() throws Exception { - List regions = getTableRegionMap().get(tableName); - if (regions == null) { - return false; - } - return regions.size() >= 6; - } - }); - - //make assignment uneven, move all regions to one server - Map> assignMap = - getTableServerRegionMap().get(tableName); - final ServerName first = assignMap.entrySet().iterator().next().getKey(); - for(RegionInfo region: admin.getTableRegions(tableName)) { - if(!assignMap.get(first).contains(region.getRegionNameAsString())) { - admin.move(region.getEncodedNameAsBytes(), Bytes.toBytes(first.getServerName())); - } + @Override + public Optional getMasterObserver() { + return Optional.of(this); } - TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate() { - @Override - public boolean evaluate() throws Exception { - Map> map = getTableServerRegionMap().get(tableName); - if (map == null) { - return true; - } - List regions = map.get(first); - if (regions == null) { - return true; - } - return regions.size() >= 6; - } - }); - //balance the other group and make sure it doesn't affect the new group - admin.setBalancerRunning(true,true); - rsGroupAdmin.balanceRSGroup(RSGroupInfo.DEFAULT_GROUP); - assertEquals(6, getTableServerRegionMap().get(tableName).get(first).size()); - - //disable balance, balancer will not be run and return false - admin.setBalancerRunning(false,true); - assertFalse(rsGroupAdmin.balanceRSGroup(newGroupName)); - assertEquals(6, getTableServerRegionMap().get(tableName).get(first).size()); - - //enable balance - admin.setBalancerRunning(true,true); - rsGroupAdmin.balanceRSGroup(newGroupName); - TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate() { - @Override - public boolean evaluate() throws Exception { - for (List regions : getTableServerRegionMap().get(tableName).values()) { - if (2 != regions.size()) { - return false; - } - } - return true; - } - }); - admin.setBalancerRunning(false,true); - } - - @Test - public void testRegionMove() throws Exception { - final RSGroupInfo newGroup = addGroup(getGroupName(name.getMethodName()), 1); - final byte[] familyNameBytes = Bytes.toBytes("f"); - // All the regions created below will be assigned to the default group. - TEST_UTIL.createMultiRegionTable(tableName, familyNameBytes, 6); - TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate() { - @Override - public boolean evaluate() throws Exception { - List regions = getTableRegionMap().get(tableName); - if (regions == null) { - return false; - } - return getTableRegionMap().get(tableName).size() >= 6; - } - }); - - //get target region to move - Map> assignMap = - getTableServerRegionMap().get(tableName); - String targetRegion = null; - for(ServerName server : assignMap.keySet()) { - targetRegion = assignMap.get(server).size() > 0 ? assignMap.get(server).get(0) : null; - if(targetRegion != null) { - break; - } - } - //get server which is not a member of new group - ServerName targetServer = null; - for (ServerName server : admin.getClusterMetrics(EnumSet.of(Option.LIVE_SERVERS)) - .getLiveServerMetrics().keySet()) { - if (!newGroup.containsServer(server.getAddress())) { - targetServer = server; - break; - } - } - - final AdminProtos.AdminService.BlockingInterface targetRS = - ((ClusterConnection) admin.getConnection()).getAdmin(targetServer); - - //move target server to group - rsGroupAdmin.moveServers(Sets.newHashSet(targetServer.getAddress()), - newGroup.getName()); - TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate() { - @Override - public boolean evaluate() throws Exception { - return ProtobufUtil.getOnlineRegions(targetRS).size() <= 0; - } - }); - - // Lets move this region to the new group. - TEST_UTIL.getAdmin().move(Bytes.toBytes(RegionInfo.encodeRegionName( - Bytes.toBytes(targetRegion))), Bytes.toBytes(targetServer.getServerName())); - TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate() { - @Override - public boolean evaluate() throws Exception { - return - getTableRegionMap().get(tableName) != null && - getTableRegionMap().get(tableName).size() == 6 && - admin.getClusterMetrics(EnumSet.of(Option.REGIONS_IN_TRANSITION)) - .getRegionStatesInTransition().size() < 1; - } - }); - - //verify that targetServer didn't open it - for (RegionInfo region: ProtobufUtil.getOnlineRegions(targetRS)) { - if (targetRegion.equals(region.getRegionNameAsString())) { - fail("Target server opened region"); - } + @Override + public void preMoveServersAndTables(final ObserverContext ctx, + Set
servers, Set tables, String targetGroup) throws IOException { + preMoveServersAndTables = true; } - } - @Test - public void testFailRemoveGroup() throws IOException, InterruptedException { - int initNumGroups = rsGroupAdmin.listRSGroups().size(); - addGroup("bar", 3); - TEST_UTIL.createTable(tableName, Bytes.toBytes("f")); - rsGroupAdmin.moveTables(Sets.newHashSet(tableName), "bar"); - RSGroupInfo barGroup = rsGroupAdmin.getRSGroupInfo("bar"); - //group is not empty therefore it should fail - try { - rsGroupAdmin.removeRSGroup(barGroup.getName()); - fail("Expected remove group to fail"); - } catch(IOException e) { - } - //group cannot lose all it's servers therefore it should fail - try { - rsGroupAdmin.moveServers(barGroup.getServers(), RSGroupInfo.DEFAULT_GROUP); - fail("Expected move servers to fail"); - } catch(IOException e) { + @Override + public void postMoveServersAndTables(final ObserverContext ctx, + Set
servers, Set tables, String targetGroup) throws IOException { + postMoveServersAndTables = true; } - rsGroupAdmin.moveTables(barGroup.getTables(), RSGroupInfo.DEFAULT_GROUP); - try { - rsGroupAdmin.removeRSGroup(barGroup.getName()); - fail("Expected move servers to fail"); - } catch(IOException e) { + @Override + public void preRemoveServers( + final ObserverContext ctx, + Set
servers) throws IOException { + preRemoveServersCalled = true; } - rsGroupAdmin.moveServers(barGroup.getServers(), RSGroupInfo.DEFAULT_GROUP); - rsGroupAdmin.removeRSGroup(barGroup.getName()); - - Assert.assertEquals(initNumGroups, rsGroupAdmin.listRSGroups().size()); - } - @Test - public void testKillRS() throws Exception { - RSGroupInfo appInfo = addGroup("appInfo", 1); - - final TableName tableName = TableName.valueOf(tablePrefix+"_ns", name.getMethodName()); - admin.createNamespace( - NamespaceDescriptor.create(tableName.getNamespaceAsString()) - .addConfiguration(RSGroupInfo.NAMESPACE_DESC_PROP_GROUP, appInfo.getName()).build()); - final HTableDescriptor desc = new HTableDescriptor(tableName); - desc.addFamily(new HColumnDescriptor("f")); - admin.createTable(desc); - //wait for created table to be assigned - TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate() { - @Override - public boolean evaluate() throws Exception { - return getTableRegionMap().get(desc.getTableName()) != null; - } - }); - - ServerName targetServer = ServerName.parseServerName( - appInfo.getServers().iterator().next().toString()); - AdminProtos.AdminService.BlockingInterface targetRS = - ((ClusterConnection) admin.getConnection()).getAdmin(targetServer); - RegionInfo targetRegion = ProtobufUtil.getOnlineRegions(targetRS).get(0); - Assert.assertEquals(1, ProtobufUtil.getOnlineRegions(targetRS).size()); - - try { - //stopping may cause an exception - //due to the connection loss - targetRS.stopServer(null, - AdminProtos.StopServerRequest.newBuilder().setReason("Die").build()); - } catch(Exception e) { + @Override + public void postRemoveServers( + final ObserverContext ctx, + Set
servers) throws IOException { + postRemoveServersCalled = true; } - assertFalse(cluster.getClusterMetrics().getLiveServerMetrics().containsKey(targetServer)); - //wait for created table to be assigned - TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate() { - @Override - public boolean evaluate() throws Exception { - return cluster.getClusterMetrics().getRegionStatesInTransition().isEmpty(); - } - }); - Set
newServers = Sets.newHashSet(); - newServers.add( - rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP).getServers().iterator().next()); - rsGroupAdmin.moveServers(newServers, appInfo.getName()); - - //Make sure all the table's regions get reassigned - //disabling the table guarantees no conflicting assign/unassign (ie SSH) happens - admin.disableTable(tableName); - admin.enableTable(tableName); - - //wait for region to be assigned - TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate() { - @Override - public boolean evaluate() throws Exception { - return cluster.getClusterMetrics().getRegionStatesInTransition().isEmpty(); - } - }); - - targetServer = ServerName.parseServerName( - newServers.iterator().next().toString()); - targetRS = - ((ClusterConnection) admin.getConnection()).getAdmin(targetServer); - Assert.assertEquals(1, ProtobufUtil.getOnlineRegions(targetRS).size()); - Assert.assertEquals(tableName, - ProtobufUtil.getOnlineRegions(targetRS).get(0).getTable()); - } - - @Test - public void testValidGroupNames() throws IOException { - String[] badNames = {"foo*","foo@","-"}; - String[] goodNames = {"foo_123"}; - - for(String entry: badNames) { - try { - rsGroupAdmin.addRSGroup(entry); - fail("Expected a constraint exception for: "+entry); - } catch(ConstraintException ex) { - //expected - } + @Override + public void preRemoveRSGroup(final ObserverContext ctx, + String name) throws IOException { + preRemoveRSGroupCalled = true; } - for(String entry: goodNames) { - rsGroupAdmin.addRSGroup(entry); - } - } - - private String getGroupName(String baseName) { - return groupPrefix+"_"+baseName+"_"+rand.nextInt(Integer.MAX_VALUE); - } - - @Test - public void testMultiTableMove() throws Exception { - final TableName tableNameA = TableName.valueOf(tablePrefix + name.getMethodName() + "A"); - final TableName tableNameB = TableName.valueOf(tablePrefix + name.getMethodName() + "B"); - final byte[] familyNameBytes = Bytes.toBytes("f"); - String newGroupName = getGroupName(name.getMethodName()); - final RSGroupInfo newGroup = addGroup(newGroupName, 1); - - TEST_UTIL.createTable(tableNameA, familyNameBytes); - TEST_UTIL.createTable(tableNameB, familyNameBytes); - TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate() { - @Override - public boolean evaluate() throws Exception { - List regionsA = getTableRegionMap().get(tableNameA); - if (regionsA == null) { - return false; - } - - List regionsB = getTableRegionMap().get(tableNameB); - if (regionsB == null) { - return false; - } - - return getTableRegionMap().get(tableNameA).size() >= 1 - && getTableRegionMap().get(tableNameB).size() >= 1; - } - }); - - RSGroupInfo tableGrpA = rsGroupAdmin.getRSGroupInfoOfTable(tableNameA); - assertTrue(tableGrpA.getName().equals(RSGroupInfo.DEFAULT_GROUP)); - - RSGroupInfo tableGrpB = rsGroupAdmin.getRSGroupInfoOfTable(tableNameB); - assertTrue(tableGrpB.getName().equals(RSGroupInfo.DEFAULT_GROUP)); - //change table's group - LOG.info("Moving table [" + tableNameA + "," + tableNameB + "] to " + newGroup.getName()); - rsGroupAdmin.moveTables(Sets.newHashSet(tableNameA, tableNameB), newGroup.getName()); - - //verify group change - Assert.assertEquals(newGroup.getName(), - rsGroupAdmin.getRSGroupInfoOfTable(tableNameA).getName()); - - Assert.assertEquals(newGroup.getName(), - rsGroupAdmin.getRSGroupInfoOfTable(tableNameB).getName()); - - //verify tables' not exist in old group - Set DefaultTables = rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP) - .getTables(); - assertFalse(DefaultTables.contains(tableNameA)); - assertFalse(DefaultTables.contains(tableNameB)); - - //verify tables' exist in new group - Set newGroupTables = rsGroupAdmin.getRSGroupInfo(newGroupName).getTables(); - assertTrue(newGroupTables.contains(tableNameA)); - assertTrue(newGroupTables.contains(tableNameB)); - } - - @Test - public void testDisabledTableMove() throws Exception { - final byte[] familyNameBytes = Bytes.toBytes("f"); - String newGroupName = getGroupName(name.getMethodName()); - final RSGroupInfo newGroup = addGroup(newGroupName, 2); - - TEST_UTIL.createMultiRegionTable(tableName, familyNameBytes, 5); - TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate() { - @Override - public boolean evaluate() throws Exception { - List regions = getTableRegionMap().get(tableName); - if (regions == null) { - return false; - } - return getTableRegionMap().get(tableName).size() >= 5; - } - }); - - RSGroupInfo tableGrp = rsGroupAdmin.getRSGroupInfoOfTable(tableName); - assertTrue(tableGrp.getName().equals(RSGroupInfo.DEFAULT_GROUP)); - - //test disable table - admin.disableTable(tableName); - - //change table's group - LOG.info("Moving table "+ tableName + " to " + newGroup.getName()); - rsGroupAdmin.moveTables(Sets.newHashSet(tableName), newGroup.getName()); - - //verify group change - Assert.assertEquals(newGroup.getName(), - rsGroupAdmin.getRSGroupInfoOfTable(tableName).getName()); - } - - @Test - public void testMoveServersAndTables() throws Exception { - LOG.info("testMoveServersAndTables"); - final RSGroupInfo newGroup = addGroup(getGroupName(name.getMethodName()), 1); - //create table - final byte[] familyNameBytes = Bytes.toBytes("f"); - TEST_UTIL.createMultiRegionTable(tableName, familyNameBytes, 5); - TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate() { - @Override - public boolean evaluate() throws Exception { - List regions = getTableRegionMap().get(tableName); - if (regions == null) { - return false; - } - return getTableRegionMap().get(tableName).size() >= 5; - } - }); - - //get server which is not a member of new group - ServerName targetServer = null; - for(ServerName server : admin.getClusterMetrics(EnumSet.of(Option.LIVE_SERVERS)) - .getLiveServerMetrics().keySet()) { - if(!newGroup.containsServer(server.getAddress()) && - !rsGroupAdmin.getRSGroupInfo("master").containsServer(server.getAddress())) { - targetServer = server; - break; - } + @Override + public void postRemoveRSGroup(final ObserverContext ctx, + String name) throws IOException { + postRemoveRSGroupCalled = true; } - LOG.debug("Print group info : " + rsGroupAdmin.listRSGroups()); - int oldDefaultGroupServerSize = - rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP).getServers().size(); - int oldDefaultGroupTableSize = - rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP).getTables().size(); - - //test fail bogus server move - try { - rsGroupAdmin.moveServersAndTables(Sets.newHashSet(Address.fromString("foo:9999")), - Sets.newHashSet(tableName), newGroup.getName()); - fail("Bogus servers shouldn't have been successfully moved."); - } catch(IOException ex) { - String exp = "Source RSGroup for server foo:9999 does not exist."; - String msg = "Expected '" + exp + "' in exception message: "; - assertTrue(msg + " " + ex.getMessage(), ex.getMessage().contains(exp)); + @Override + public void preAddRSGroup(final ObserverContext ctx, + String name) throws IOException { + preAddRSGroupCalled = true; } - //test fail server move - try { - rsGroupAdmin.moveServersAndTables(Sets.newHashSet(targetServer.getAddress()), - Sets.newHashSet(tableName), RSGroupInfo.DEFAULT_GROUP); - fail("servers shouldn't have been successfully moved."); - } catch(IOException ex) { - String exp = "Target RSGroup " + RSGroupInfo.DEFAULT_GROUP + - " is same as source " + RSGroupInfo.DEFAULT_GROUP + " RSGroup."; - String msg = "Expected '" + exp + "' in exception message: "; - assertTrue(msg + " " + ex.getMessage(), ex.getMessage().contains(exp)); + @Override + public void postAddRSGroup(final ObserverContext ctx, + String name) throws IOException { + postAddRSGroupCalled = true; } - //verify default group info - Assert.assertEquals(oldDefaultGroupServerSize, - rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP).getServers().size()); - Assert.assertEquals(oldDefaultGroupTableSize, - rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP).getTables().size()); - - //verify new group info - Assert.assertEquals(1, - rsGroupAdmin.getRSGroupInfo(newGroup.getName()).getServers().size()); - Assert.assertEquals(0, - rsGroupAdmin.getRSGroupInfo(newGroup.getName()).getTables().size()); - - //get all region to move targetServer - List regionList = getTableRegionMap().get(tableName); - for(String region : regionList) { - // Lets move this region to the targetServer - TEST_UTIL.getAdmin().move(Bytes.toBytes(RegionInfo.encodeRegionName(Bytes.toBytes(region))), - Bytes.toBytes(targetServer.getServerName())); + @Override + public void preMoveTables(final ObserverContext ctx, + Set tables, String targetGroup) throws IOException { + preMoveTablesCalled = true; } - TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate() { - @Override - public boolean evaluate() throws Exception { - return getTableRegionMap().get(tableName) != null && - getTableRegionMap().get(tableName).size() == 5 && - getTableServerRegionMap().get(tableName).size() == 1 && - admin.getClusterMetrics(EnumSet.of(Option.REGIONS_IN_TRANSITION)) - .getRegionStatesInTransition().size() < 1; - } - }); - - //verify that all region move to targetServer - Assert.assertEquals(5, getTableServerRegionMap().get(tableName).get(targetServer).size()); - - //move targetServer and table to newGroup - LOG.info("moving server and table to newGroup"); - rsGroupAdmin.moveServersAndTables(Sets.newHashSet(targetServer.getAddress()), - Sets.newHashSet(tableName), newGroup.getName()); - - //verify group change - Assert.assertEquals(newGroup.getName(), - rsGroupAdmin.getRSGroupInfoOfTable(tableName).getName()); - - //verify servers' not exist in old group - Set
defaultServers = rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP) - .getServers(); - assertFalse(defaultServers.contains(targetServer.getAddress())); - - //verify servers' exist in new group - Set
newGroupServers = rsGroupAdmin.getRSGroupInfo(newGroup.getName()).getServers(); - assertTrue(newGroupServers.contains(targetServer.getAddress())); - - //verify tables' not exist in old group - Set defaultTables = rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP) - .getTables(); - assertFalse(defaultTables.contains(tableName)); - - //verify tables' exist in new group - Set newGroupTables = rsGroupAdmin.getRSGroupInfo(newGroup.getName()).getTables(); - assertTrue(newGroupTables.contains(tableName)); - - //verify that all region still assgin on targetServer - Assert.assertEquals(5, getTableServerRegionMap().get(tableName).get(targetServer).size()); - } - - @Test - public void testClearDeadServers() throws Exception { - LOG.info("testClearDeadServers"); - final RSGroupInfo newGroup = addGroup(getGroupName(name.getMethodName()), 3); - NUM_DEAD_SERVERS = cluster.getClusterMetrics().getDeadServerNames().size(); - - ServerName targetServer = ServerName.parseServerName( - newGroup.getServers().iterator().next().toString()); - AdminProtos.AdminService.BlockingInterface targetRS = - ((ClusterConnection) admin.getConnection()).getAdmin(targetServer); - try { - targetServer = ProtobufUtil.toServerName(targetRS.getServerInfo(null, - GetServerInfoRequest.newBuilder().build()).getServerInfo().getServerName()); - //stopping may cause an exception - //due to the connection loss - targetRS.stopServer(null, - AdminProtos.StopServerRequest.newBuilder().setReason("Die").build()); - NUM_DEAD_SERVERS ++; - } catch(Exception e) { + @Override + public void postMoveTables(final ObserverContext ctx, + Set tables, String targetGroup) throws IOException { + postMoveTablesCalled = true; } - //wait for stopped regionserver to dead server list - TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate() { - @Override - public boolean evaluate() throws Exception { - return !master.getServerManager().areDeadServersInProgress() - && cluster.getClusterMetrics().getDeadServerNames().size() == NUM_DEAD_SERVERS; - } - }); - assertFalse(cluster.getClusterMetrics().getLiveServerMetrics().containsKey(targetServer)); - assertTrue(cluster.getClusterMetrics().getDeadServerNames().contains(targetServer)); - assertTrue(newGroup.getServers().contains(targetServer.getAddress())); - //clear dead servers list - List notClearedServers = admin.clearDeadServers(Lists.newArrayList(targetServer)); - assertEquals(0, notClearedServers.size()); - - Set
newGroupServers = rsGroupAdmin.getRSGroupInfo(newGroup.getName()).getServers(); - assertFalse(newGroupServers.contains(targetServer.getAddress())); - assertEquals(2, newGroupServers.size()); - } - - @Test - public void testRemoveServers() throws Exception { - LOG.info("testRemoveServers"); - final RSGroupInfo newGroup = addGroup(getGroupName(name.getMethodName()), 3); - Iterator
iterator = newGroup.getServers().iterator(); - ServerName targetServer = ServerName.parseServerName(iterator.next().toString()); - - // remove online servers - try { - rsGroupAdmin.removeServers(Sets.newHashSet(targetServer.getAddress())); - fail("Online servers shouldn't have been successfully removed."); - } catch(IOException ex) { - String exp = "Server " + targetServer.getAddress() - + " is an online server, not allowed to remove."; - String msg = "Expected '" + exp + "' in exception message: "; - assertTrue(msg + " " + ex.getMessage(), ex.getMessage().contains(exp)); + @Override + public void preMoveServers(final ObserverContext ctx, + Set
servers, String targetGroup) throws IOException { + preMoveServersCalled = true; } - assertTrue(newGroup.getServers().contains(targetServer.getAddress())); - // remove dead servers - NUM_DEAD_SERVERS = cluster.getClusterMetrics().getDeadServerNames().size(); - AdminProtos.AdminService.BlockingInterface targetRS = - ((ClusterConnection) admin.getConnection()).getAdmin(targetServer); - try { - targetServer = ProtobufUtil.toServerName(targetRS.getServerInfo(null, - GetServerInfoRequest.newBuilder().build()).getServerInfo().getServerName()); - //stopping may cause an exception - //due to the connection loss - LOG.info("stopping server " + targetServer.getHostAndPort()); - targetRS.stopServer(null, - AdminProtos.StopServerRequest.newBuilder().setReason("Die").build()); - NUM_DEAD_SERVERS ++; - } catch(Exception e) { + @Override + public void postMoveServers(final ObserverContext ctx, + Set
servers, String targetGroup) throws IOException { + postMoveServersCalled = true; } - //wait for stopped regionserver to dead server list - TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate() { - @Override - public boolean evaluate() throws Exception { - return !master.getServerManager().areDeadServersInProgress() - && cluster.getClusterMetrics().getDeadServerNames().size() == NUM_DEAD_SERVERS; - } - }); - - try { - rsGroupAdmin.removeServers(Sets.newHashSet(targetServer.getAddress())); - fail("Dead servers shouldn't have been successfully removed."); - } catch(IOException ex) { - String exp = "Server " + targetServer.getAddress() + " is on the dead servers list," - + " Maybe it will come back again, not allowed to remove."; - String msg = "Expected '" + exp + "' in exception message: "; - assertTrue(msg + " " + ex.getMessage(), ex.getMessage().contains(exp)); + @Override + public void preBalanceRSGroup(final ObserverContext ctx, + String groupName) throws IOException { + preBalanceRSGroupCalled = true; } - assertTrue(newGroup.getServers().contains(targetServer.getAddress())); - - // remove decommissioned servers - List serversToDecommission = new ArrayList<>(); - targetServer = ServerName.parseServerName(iterator.next().toString()); - targetRS = ((ClusterConnection) admin.getConnection()).getAdmin(targetServer); - targetServer = ProtobufUtil.toServerName(targetRS.getServerInfo(null, - GetServerInfoRequest.newBuilder().build()).getServerInfo().getServerName()); - assertTrue(master.getServerManager().getOnlineServers().containsKey(targetServer)); - serversToDecommission.add(targetServer); - - admin.decommissionRegionServers(serversToDecommission, true); - assertEquals(1, admin.listDecommissionedRegionServers().size()); - assertTrue(newGroup.getServers().contains(targetServer.getAddress())); - rsGroupAdmin.removeServers(Sets.newHashSet(targetServer.getAddress())); - Set
newGroupServers = rsGroupAdmin.getRSGroupInfo(newGroup.getName()).getServers(); - assertFalse(newGroupServers.contains(targetServer.getAddress())); - assertEquals(2, newGroupServers.size()); - } - - @Test - public void testCreateWhenRsgroupNoOnlineServers() throws Exception { - LOG.info("testCreateWhenRsgroupNoOnlineServers"); - - // set rsgroup has no online servers and test create table - final RSGroupInfo appInfo = addGroup("appInfo", 1); - Iterator
iterator = appInfo.getServers().iterator(); - List serversToDecommission = new ArrayList<>(); - ServerName targetServer = ServerName.parseServerName(iterator.next().toString()); - AdminProtos.AdminService.BlockingInterface targetRS = - ((ClusterConnection) admin.getConnection()).getAdmin(targetServer); - targetServer = ProtobufUtil.toServerName( - targetRS.getServerInfo(null, GetServerInfoRequest.newBuilder().build()).getServerInfo() - .getServerName()); - assertTrue(master.getServerManager().getOnlineServers().containsKey(targetServer)); - serversToDecommission.add(targetServer); - admin.decommissionRegionServers(serversToDecommission, true); - assertEquals(1, admin.listDecommissionedRegionServers().size()); - - final TableName tableName = TableName.valueOf(tablePrefix + "_ns", name.getMethodName()); - admin.createNamespace(NamespaceDescriptor.create(tableName.getNamespaceAsString()) - .addConfiguration(RSGroupInfo.NAMESPACE_DESC_PROP_GROUP, appInfo.getName()).build()); - final HTableDescriptor desc = new HTableDescriptor(tableName); - desc.addFamily(new HColumnDescriptor("f")); - try { - admin.createTable(desc); - fail("Shouldn't create table successfully!"); - } catch (Exception e) { - LOG.debug("create table error", e); + @Override + public void postBalanceRSGroup(final ObserverContext ctx, + String groupName, boolean balancerRan) throws IOException { + postBalanceRSGroupCalled = true; } - - // recommission and test create table - admin.recommissionRegionServer(targetServer, null); - assertEquals(0, admin.listDecommissionedRegionServers().size()); - admin.createTable(desc); - // wait for created table to be assigned - TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate() { - @Override public boolean evaluate() throws Exception { - return getTableRegionMap().get(desc.getTableName()) != null; - } - }); } } diff --git a/hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupsBasics.java b/hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupsBasics.java new file mode 100644 index 0000000000..ff1b313d40 --- /dev/null +++ b/hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupsBasics.java @@ -0,0 +1,320 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.hadoop.hbase.rsgroup; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.hadoop.hbase.HBaseClassTestRule; +import org.apache.hadoop.hbase.HColumnDescriptor; +import org.apache.hadoop.hbase.HTableDescriptor; +import org.apache.hadoop.hbase.NamespaceDescriptor; +import org.apache.hadoop.hbase.ServerName; +import org.apache.hadoop.hbase.TableName; +import org.apache.hadoop.hbase.Waiter; +import org.apache.hadoop.hbase.client.ClusterConnection; +import org.apache.hadoop.hbase.net.Address; +import org.apache.hadoop.hbase.quotas.QuotaTableUtil; +import org.apache.hadoop.hbase.quotas.QuotaUtil; +import org.apache.hadoop.hbase.util.Bytes; + +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.ClassRule; +import org.junit.Test; +import org.junit.experimental.categories.Category; + +import org.apache.hbase.thirdparty.com.google.common.collect.Lists; +import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil; +import org.apache.hadoop.hbase.shaded.protobuf.generated.AdminProtos; +import org.apache.hadoop.hbase.shaded.protobuf.generated.AdminProtos.GetServerInfoRequest; +import org.apache.hadoop.hbase.testclassification.MediumTests; + +@Category({MediumTests.class}) +public class TestRSGroupsBasics extends TestRSGroupsBase { + + @ClassRule + public static final HBaseClassTestRule CLASS_RULE = + HBaseClassTestRule.forClass(TestRSGroupsBasics.class); + + protected static final Log LOG = LogFactory.getLog(TestRSGroupsBasics.class); + + @BeforeClass + public static void setUp() throws Exception { + setUpTestBeforeClass(); + } + + @AfterClass + public static void tearDown() throws Exception { + tearDownAfterClass(); + } + + @Before + public void beforeMethod() throws Exception { + setUpBeforeMethod(); + } + + @After + public void afterMethod() throws Exception { + tearDownAfterMethod(); + } + + @Test + public void testBasicStartUp() throws IOException { + RSGroupInfo defaultInfo = rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP); + assertEquals(4, defaultInfo.getServers().size()); + // Assignment of root and meta regions. + int count = master.getAssignmentManager().getRegionStates().getRegionAssignments().size(); + //3 meta,namespace, group + assertEquals(3, count); + } + + @Test + public void testCreateAndDrop() throws Exception { + TEST_UTIL.createTable(tableName, Bytes.toBytes("cf")); + //wait for created table to be assigned + TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate() { + @Override + public boolean evaluate() throws Exception { + return getTableRegionMap().get(tableName) != null; + } + }); + TEST_UTIL.deleteTable(tableName); + } + + @Test + public void testCreateMultiRegion() throws IOException { + byte[] end = {1,3,5,7,9}; + byte[] start = {0,2,4,6,8}; + byte[][] f = {Bytes.toBytes("f")}; + TEST_UTIL.createTable(tableName, f,1,start,end,10); + } + + @Test + public void testNamespaceCreateAndAssign() throws Exception { + LOG.info("testNamespaceCreateAndAssign"); + String nsName = tablePrefix+"_foo"; + final TableName tableName = TableName.valueOf(nsName, tablePrefix + "_testCreateAndAssign"); + RSGroupInfo appInfo = addGroup("appInfo", 1); + admin.createNamespace(NamespaceDescriptor.create(nsName) + .addConfiguration(RSGroupInfo.NAMESPACE_DESC_PROP_GROUP, "appInfo").build()); + final HTableDescriptor desc = new HTableDescriptor(tableName); + desc.addFamily(new HColumnDescriptor("f")); + admin.createTable(desc); + //wait for created table to be assigned + TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate() { + @Override + public boolean evaluate() throws Exception { + return getTableRegionMap().get(desc.getTableName()) != null; + } + }); + ServerName targetServer = + ServerName.parseServerName(appInfo.getServers().iterator().next().toString()); + AdminProtos.AdminService.BlockingInterface rs = + ((ClusterConnection) admin.getConnection()).getAdmin(targetServer); + //verify it was assigned to the right group + Assert.assertEquals(1, ProtobufUtil.getOnlineRegions(rs).size()); + } + + @Test + public void testCreateWhenRsgroupNoOnlineServers() throws Exception { + LOG.info("testCreateWhenRsgroupNoOnlineServers"); + + // set rsgroup has no online servers and test create table + final RSGroupInfo appInfo = addGroup("appInfo", 1); + Iterator
iterator = appInfo.getServers().iterator(); + List serversToDecommission = new ArrayList<>(); + ServerName targetServer = ServerName.parseServerName(iterator.next().toString()); + AdminProtos.AdminService.BlockingInterface targetRS = + ((ClusterConnection) admin.getConnection()).getAdmin(targetServer); + targetServer = ProtobufUtil.toServerName( + targetRS.getServerInfo(null, GetServerInfoRequest.newBuilder().build()).getServerInfo() + .getServerName()); + assertTrue(master.getServerManager().getOnlineServers().containsKey(targetServer)); + serversToDecommission.add(targetServer); + admin.decommissionRegionServers(serversToDecommission, true); + assertEquals(1, admin.listDecommissionedRegionServers().size()); + + final TableName tableName = TableName.valueOf(tablePrefix + "_ns", name.getMethodName()); + admin.createNamespace(NamespaceDescriptor.create(tableName.getNamespaceAsString()) + .addConfiguration(RSGroupInfo.NAMESPACE_DESC_PROP_GROUP, appInfo.getName()).build()); + final HTableDescriptor desc = new HTableDescriptor(tableName); + desc.addFamily(new HColumnDescriptor("f")); + try { + admin.createTable(desc); + fail("Shouldn't create table successfully!"); + } catch (Exception e) { + LOG.debug("create table error", e); + } + + // recommission and test create table + admin.recommissionRegionServer(targetServer, null); + assertEquals(0, admin.listDecommissionedRegionServers().size()); + admin.createTable(desc); + // wait for created table to be assigned + TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate() { + @Override public boolean evaluate() throws Exception { + return getTableRegionMap().get(desc.getTableName()) != null; + } + }); + } + + @Test + public void testDefaultNamespaceCreateAndAssign() throws Exception { + LOG.info("testDefaultNamespaceCreateAndAssign"); + String tableName = tablePrefix + "_testCreateAndAssign"; + admin.modifyNamespace(NamespaceDescriptor.create("default") + .addConfiguration(RSGroupInfo.NAMESPACE_DESC_PROP_GROUP, "default").build()); + final HTableDescriptor desc = new HTableDescriptor(TableName.valueOf(tableName)); + desc.addFamily(new HColumnDescriptor("f")); + admin.createTable(desc); + //wait for created table to be assigned + TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate() { + @Override + public boolean evaluate() throws Exception { + return getTableRegionMap().get(desc.getTableName()) != null; + } + }); + } + + @Test + public void testCloneSnapshot() throws Exception { + byte[] FAMILY = Bytes.toBytes("test"); + String snapshotName = tableName.getNameAsString() + "_snap"; + TableName clonedTableName = TableName.valueOf(tableName.getNameAsString() + "_clone"); + + // create base table + TEST_UTIL.createTable(tableName, FAMILY); + + // create snapshot + admin.snapshot(snapshotName, tableName); + + // clone + admin.cloneSnapshot(snapshotName, clonedTableName); + } + + @Test + public void testClearDeadServers() throws Exception { + LOG.info("testClearDeadServers"); + final RSGroupInfo newGroup = addGroup(getGroupName(name.getMethodName()), 3); + NUM_DEAD_SERVERS = cluster.getClusterMetrics().getDeadServerNames().size(); + + ServerName targetServer = ServerName.parseServerName( + newGroup.getServers().iterator().next().toString()); + AdminProtos.AdminService.BlockingInterface targetRS = + ((ClusterConnection) admin.getConnection()).getAdmin(targetServer); + try { + targetServer = ProtobufUtil.toServerName(targetRS.getServerInfo(null, + GetServerInfoRequest.newBuilder().build()).getServerInfo().getServerName()); + //stopping may cause an exception + //due to the connection loss + targetRS.stopServer(null, + AdminProtos.StopServerRequest.newBuilder().setReason("Die").build()); + NUM_DEAD_SERVERS ++; + } catch(Exception e) { + } + //wait for stopped regionserver to dead server list + TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate() { + @Override + public boolean evaluate() throws Exception { + return !master.getServerManager().areDeadServersInProgress() + && cluster.getClusterMetrics().getDeadServerNames().size() == NUM_DEAD_SERVERS; + } + }); + assertFalse(cluster.getClusterMetrics().getLiveServerMetrics().containsKey(targetServer)); + assertTrue(cluster.getClusterMetrics().getDeadServerNames().contains(targetServer)); + assertTrue(newGroup.getServers().contains(targetServer.getAddress())); + + //clear dead servers list + List notClearedServers = admin.clearDeadServers(Lists.newArrayList(targetServer)); + assertEquals(0, notClearedServers.size()); + + Set
newGroupServers = rsGroupAdmin.getRSGroupInfo(newGroup.getName()).getServers(); + assertFalse(newGroupServers.contains(targetServer.getAddress())); + assertEquals(2, newGroupServers.size()); + } + + @Test + public void testClearNotProcessedDeadServer() throws Exception { + LOG.info("testClearNotProcessedDeadServer"); + NUM_DEAD_SERVERS = cluster.getClusterMetrics().getDeadServerNames().size(); + RSGroupInfo appInfo = addGroup("deadServerGroup", 1); + ServerName targetServer = + ServerName.parseServerName(appInfo.getServers().iterator().next().toString()); + AdminProtos.AdminService.BlockingInterface targetRS = + ((ClusterConnection) admin.getConnection()).getAdmin(targetServer); + try { + targetServer = ProtobufUtil.toServerName(targetRS.getServerInfo(null, + AdminProtos.GetServerInfoRequest.newBuilder().build()).getServerInfo().getServerName()); + //stopping may cause an exception + //due to the connection loss + targetRS.stopServer(null, + AdminProtos.StopServerRequest.newBuilder().setReason("Die").build()); + NUM_DEAD_SERVERS ++; + } catch(Exception e) { + } + TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate() { + @Override + public boolean evaluate() throws Exception { + return cluster.getClusterMetrics().getDeadServerNames().size() == NUM_DEAD_SERVERS; + } + }); + List notClearedServers = + admin.clearDeadServers(Lists.newArrayList(targetServer)); + assertEquals(1, notClearedServers.size()); + } + + @Test + public void testRSGroupsWithHBaseQuota() throws Exception { + TEST_UTIL.getConfiguration().setBoolean(QuotaUtil.QUOTA_CONF_KEY, true); + restartHBaseCluster(); + try { + TEST_UTIL.waitFor(90000, new Waiter.Predicate() { + @Override + public boolean evaluate() throws Exception { + return admin.isTableAvailable(QuotaTableUtil.QUOTA_TABLE_NAME); + } + }); + } finally { + TEST_UTIL.getConfiguration().setBoolean(QuotaUtil.QUOTA_CONF_KEY, false); + restartHBaseCluster(); + } + } + + private void restartHBaseCluster() throws Exception { + LOG.info("\n\nShutting down cluster"); + TEST_UTIL.shutdownMiniHBaseCluster(); + LOG.info("\n\nSleeping a bit"); + Thread.sleep(2000); + TEST_UTIL.restartHBaseCluster(NUM_SLAVES_BASE - 1); + initialize(); + } +} diff --git a/hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupsKillRS.java b/hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupsKillRS.java new file mode 100644 index 0000000000..1bd6eae9cb --- /dev/null +++ b/hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupsKillRS.java @@ -0,0 +1,149 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.hadoop.hbase.rsgroup; + +import static org.junit.Assert.assertFalse; + +import java.util.Set; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.hadoop.hbase.HBaseClassTestRule; +import org.apache.hadoop.hbase.HColumnDescriptor; +import org.apache.hadoop.hbase.HTableDescriptor; +import org.apache.hadoop.hbase.NamespaceDescriptor; +import org.apache.hadoop.hbase.ServerName; +import org.apache.hadoop.hbase.TableName; +import org.apache.hadoop.hbase.Waiter; +import org.apache.hadoop.hbase.client.ClusterConnection; +import org.apache.hadoop.hbase.client.RegionInfo; +import org.apache.hadoop.hbase.net.Address; + +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.ClassRule; +import org.junit.Test; +import org.junit.experimental.categories.Category; + +import org.apache.hbase.thirdparty.com.google.common.collect.Sets; +import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil; +import org.apache.hadoop.hbase.shaded.protobuf.generated.AdminProtos; +import org.apache.hadoop.hbase.testclassification.MediumTests; + +@Category({MediumTests.class}) +public class TestRSGroupsKillRS extends TestRSGroupsBase { + + @ClassRule + public static final HBaseClassTestRule CLASS_RULE = + HBaseClassTestRule.forClass(TestRSGroupsKillRS.class); + + protected static final Log LOG = LogFactory.getLog(TestRSGroupsKillRS.class); + + @BeforeClass + public static void setUp() throws Exception { + setUpTestBeforeClass(); + } + + @AfterClass + public static void tearDown() throws Exception { + tearDownAfterClass(); + } + + @Before + public void beforeMethod() throws Exception { + setUpBeforeMethod(); + } + + @After + public void afterMethod() throws Exception { + tearDownAfterMethod(); + } + + @Test + public void testKillRS() throws Exception { + RSGroupInfo appInfo = addGroup("appInfo", 1); + + final TableName tableName = TableName.valueOf(tablePrefix+"_ns", name.getMethodName()); + admin.createNamespace( + NamespaceDescriptor.create(tableName.getNamespaceAsString()) + .addConfiguration(RSGroupInfo.NAMESPACE_DESC_PROP_GROUP, appInfo.getName()).build()); + final HTableDescriptor desc = new HTableDescriptor(tableName); + desc.addFamily(new HColumnDescriptor("f")); + admin.createTable(desc); + //wait for created table to be assigned + TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate() { + @Override + public boolean evaluate() throws Exception { + return getTableRegionMap().get(desc.getTableName()) != null; + } + }); + + ServerName targetServer = ServerName.parseServerName( + appInfo.getServers().iterator().next().toString()); + AdminProtos.AdminService.BlockingInterface targetRS = + ((ClusterConnection) admin.getConnection()).getAdmin(targetServer); + RegionInfo targetRegion = ProtobufUtil.getOnlineRegions(targetRS).get(0); + Assert.assertEquals(1, ProtobufUtil.getOnlineRegions(targetRS).size()); + + try { + //stopping may cause an exception + //due to the connection loss + targetRS.stopServer(null, + AdminProtos.StopServerRequest.newBuilder().setReason("Die").build()); + } catch(Exception e) { + } + assertFalse(cluster.getClusterMetrics().getLiveServerMetrics().containsKey(targetServer)); + + //wait for created table to be assigned + TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate() { + @Override + public boolean evaluate() throws Exception { + return cluster.getClusterMetrics().getRegionStatesInTransition().isEmpty(); + } + }); + Set
newServers = Sets.newHashSet(); + newServers.add( + rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP).getServers().iterator().next()); + rsGroupAdmin.moveServers(newServers, appInfo.getName()); + + //Make sure all the table's regions get reassigned + //disabling the table guarantees no conflicting assign/unassign (ie SSH) happens + admin.disableTable(tableName); + admin.enableTable(tableName); + + //wait for region to be assigned + TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate() { + @Override + public boolean evaluate() throws Exception { + return cluster.getClusterMetrics().getRegionStatesInTransition().isEmpty(); + } + }); + + targetServer = ServerName.parseServerName( + newServers.iterator().next().toString()); + targetRS = + ((ClusterConnection) admin.getConnection()).getAdmin(targetServer); + Assert.assertEquals(1, ProtobufUtil.getOnlineRegions(targetRS).size()); + Assert.assertEquals(tableName, + ProtobufUtil.getOnlineRegions(targetRS).get(0).getTable()); + } + +} -- 2.16.3