blob: b4b62cef435ddc37cd9f187c156e018579a77a66 [file] [log] [blame]
/**
* 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.apache.hadoop.hbase.util.Threads.sleep;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.hadoop.hbase.HBaseClassTestRule;
import org.apache.hadoop.hbase.NamespaceDescriptor;
import org.apache.hadoop.hbase.ServerName;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.Version;
import org.apache.hadoop.hbase.Waiter;
import org.apache.hadoop.hbase.client.ColumnFamilyDescriptorBuilder;
import org.apache.hadoop.hbase.client.RegionInfo;
import org.apache.hadoop.hbase.client.Table;
import org.apache.hadoop.hbase.client.TableDescriptor;
import org.apache.hadoop.hbase.client.TableDescriptorBuilder;
import org.apache.hadoop.hbase.master.procedure.ServerCrashProcedure;
import org.apache.hadoop.hbase.net.Address;
import org.apache.hadoop.hbase.testclassification.MediumTests;
import org.apache.hadoop.hbase.testclassification.RSGroupTests;
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.hadoop.hbase.util.JVMClusterUtil;
import org.apache.hadoop.hbase.util.VersionInfo;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.hbase.thirdparty.com.google.common.collect.Sets;
@Category({ RSGroupTests.class, MediumTests.class })
public class TestRSGroupsKillRS extends TestRSGroupsBase {
@ClassRule
public static final HBaseClassTestRule CLASS_RULE =
HBaseClassTestRule.forClass(TestRSGroupsKillRS.class);
private static final Logger LOG = LoggerFactory.getLogger(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(TABLE_PREFIX + "_ns", getNameWithoutIndex(name.getMethodName()));
ADMIN.createNamespace(NamespaceDescriptor.create(tableName.getNamespaceAsString())
.addConfiguration(RSGroupInfo.NAMESPACE_DESC_PROP_GROUP, appInfo.getName()).build());
final TableDescriptor desc = TableDescriptorBuilder.newBuilder(tableName)
.setColumnFamily(ColumnFamilyDescriptorBuilder.of("f")).build();
ADMIN.createTable(desc);
// wait for created table to be assigned
TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
@Override
public boolean evaluate() throws Exception {
return getTableRegionMap().get(desc.getTableName()) != null;
}
});
ServerName targetServer = getServerName(appInfo.getServers().iterator().next());
assertEquals(1, ADMIN.getRegions(targetServer).size());
try {
// stopping may cause an exception
// due to the connection loss
ADMIN.stopRegionServer(targetServer.getAddress().toString());
} catch (Exception e) {
}
// wait until the server is actually down
TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
@Override
public boolean evaluate() throws Exception {
return !CLUSTER.getClusterMetrics().getLiveServerMetrics().containsKey(targetServer);
}
});
// there is only one rs in the group and we killed it, so the region can not be online, until
// later we add new servers to it.
TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
@Override
public boolean evaluate() throws Exception {
return !CLUSTER.getClusterMetrics().getRegionStatesInTransition().isEmpty();
}
});
Set<Address> newServers = Sets.newHashSet();
newServers.add(ADMIN.getRSGroup(RSGroupInfo.DEFAULT_GROUP).getServers().iterator().next());
ADMIN.moveServersToRSGroup(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<Exception>() {
@Override
public boolean evaluate() throws Exception {
return CLUSTER.getClusterMetrics().getRegionStatesInTransition().isEmpty();
}
});
ServerName targetServer1 = getServerName(newServers.iterator().next());
assertEquals(1, ADMIN.getRegions(targetServer1).size());
assertEquals(tableName, ADMIN.getRegions(targetServer1).get(0).getTable());
}
@Test
public void testKillAllRSInGroup() throws Exception {
// create a rsgroup and move two regionservers to it
String groupName = "my_group";
int groupRSCount = 2;
addGroup(groupName, groupRSCount);
// create a table, and move it to my_group
Table t = TEST_UTIL.createMultiRegionTable(tableName, Bytes.toBytes("f"), 5);
TEST_UTIL.loadTable(t, Bytes.toBytes("f"));
Set<TableName> toAddTables = new HashSet<>();
toAddTables.add(tableName);
ADMIN.setRSGroup(toAddTables, groupName);
assertTrue(
ADMIN.getConfiguredNamespacesAndTablesInRSGroup(groupName).getSecond().contains(tableName));
TEST_UTIL.waitTableAvailable(tableName, 30000);
// check my_group servers and table regions
Set<Address> servers = ADMIN.getRSGroup(groupName).getServers();
assertEquals(2, servers.size());
LOG.debug("group servers {}", servers);
for (RegionInfo tr : MASTER.getAssignmentManager().getRegionStates()
.getRegionsOfTable(tableName)) {
assertTrue(servers.contains(MASTER.getAssignmentManager().getRegionStates()
.getRegionAssignments().get(tr).getAddress()));
}
// Move a region, to ensure there exists a region whose 'lastHost' is in my_group
// ('lastHost' of other regions are in 'default' group)
// and check if all table regions are online
List<ServerName> gsn = new ArrayList<>();
for (Address addr : servers) {
gsn.add(getServerName(addr));
}
assertEquals(2, gsn.size());
for (Map.Entry<RegionInfo, ServerName> entry : MASTER.getAssignmentManager().getRegionStates()
.getRegionAssignments().entrySet()) {
if (entry.getKey().getTable().equals(tableName)) {
LOG.debug("move region {} from {} to {}", entry.getKey().getRegionNameAsString(),
entry.getValue(), gsn.get(1 - gsn.indexOf(entry.getValue())));
TEST_UTIL.moveRegionAndWait(entry.getKey(), gsn.get(1 - gsn.indexOf(entry.getValue())));
break;
}
}
TEST_UTIL.waitTableAvailable(tableName, 30000);
// case 1: stop all the regionservers in my_group, and restart a regionserver in my_group,
// and then check if all table regions are online
for (Address addr : ADMIN.getRSGroup(groupName).getServers()) {
TEST_UTIL.getMiniHBaseCluster().stopRegionServer(getServerName(addr));
}
// better wait for a while for region reassign
sleep(10000);
assertEquals(NUM_SLAVES_BASE - gsn.size(),
TEST_UTIL.getMiniHBaseCluster().getLiveRegionServerThreads().size());
TEST_UTIL.getMiniHBaseCluster().startRegionServer(gsn.get(0).getHostname(),
gsn.get(0).getPort());
assertEquals(NUM_SLAVES_BASE - gsn.size() + 1,
TEST_UTIL.getMiniHBaseCluster().getLiveRegionServerThreads().size());
TEST_UTIL.waitTableAvailable(tableName, 30000);
// case 2: stop all the regionservers in my_group, and move another
// regionserver(from the 'default' group) to my_group,
// and then check if all table regions are online
for (JVMClusterUtil.RegionServerThread rst : TEST_UTIL.getMiniHBaseCluster()
.getLiveRegionServerThreads()) {
if (rst.getRegionServer().getServerName().getAddress().equals(gsn.get(0).getAddress())) {
TEST_UTIL.getMiniHBaseCluster().stopRegionServer(rst.getRegionServer().getServerName());
break;
}
}
sleep(10000);
assertEquals(NUM_SLAVES_BASE - gsn.size(),
TEST_UTIL.getMiniHBaseCluster().getLiveRegionServerThreads().size());
ServerName newServer = MASTER.getServerManager().getOnlineServersList().get(0);
ADMIN.moveServersToRSGroup(Sets.newHashSet(newServer.getAddress()), groupName);
// wait and check if table regions are online
TEST_UTIL.waitTableAvailable(tableName, 30000);
}
@Test
public void testLowerMetaGroupVersion() throws Exception {
// create a rsgroup and move one regionserver to it
String groupName = "meta_group";
int groupRSCount = 1;
addGroup(groupName, groupRSCount);
// move hbase:meta to meta_group
Set<TableName> toAddTables = new HashSet<>();
toAddTables.add(TableName.META_TABLE_NAME);
ADMIN.setRSGroup(toAddTables, groupName);
assertTrue(ADMIN.getConfiguredNamespacesAndTablesInRSGroup(groupName).getSecond()
.contains(TableName.META_TABLE_NAME));
// restart the regionserver in meta_group, and lower its version
String originVersion = "";
Set<Address> servers = new HashSet<>();
for (Address addr : ADMIN.getRSGroup(groupName).getServers()) {
servers.add(addr);
TEST_UTIL.getMiniHBaseCluster().stopRegionServer(getServerName(addr));
originVersion = MASTER.getRegionServerVersion(getServerName(addr));
}
// better wait for a while for region reassign
sleep(10000);
assertEquals(NUM_SLAVES_BASE - groupRSCount,
TEST_UTIL.getMiniHBaseCluster().getLiveRegionServerThreads().size());
Address address = servers.iterator().next();
int majorVersion = VersionInfo.getMajorVersion(originVersion);
assertTrue(majorVersion >= 1);
String lowerVersion = String.valueOf(majorVersion - 1) + originVersion.split("\\.")[1];
setFinalStatic(Version.class.getField("version"), lowerVersion);
TEST_UTIL.getMiniHBaseCluster().startRegionServer(address.getHostname(), address.getPort());
assertEquals(NUM_SLAVES_BASE,
TEST_UTIL.getMiniHBaseCluster().getLiveRegionServerThreads().size());
assertTrue(VersionInfo.compareVersion(originVersion,
MASTER.getRegionServerVersion(getServerName(servers.iterator().next()))) > 0);
LOG.debug("wait for META assigned...");
// SCP finished, which means all regions assigned too.
TEST_UTIL.waitFor(60000, () -> !TEST_UTIL.getHBaseCluster().getMaster().getProcedures().stream()
.filter(p -> (p instanceof ServerCrashProcedure)).findAny().isPresent());
}
private static void setFinalStatic(Field field, Object newValue) throws Exception {
field.setAccessible(true);
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
field.set(null, newValue);
}
}