001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.hadoop.hbase.rsgroup; 019 020import static org.junit.Assert.assertEquals; 021import static org.junit.Assert.assertFalse; 022import static org.junit.Assert.assertTrue; 023 024import java.util.List; 025import java.util.Map; 026import org.apache.hadoop.hbase.HBaseClassTestRule; 027import org.apache.hadoop.hbase.HConstants; 028import org.apache.hadoop.hbase.NamespaceDescriptor; 029import org.apache.hadoop.hbase.ServerName; 030import org.apache.hadoop.hbase.TableName; 031import org.apache.hadoop.hbase.Waiter; 032import org.apache.hadoop.hbase.Waiter.Predicate; 033import org.apache.hadoop.hbase.client.BalanceRequest; 034import org.apache.hadoop.hbase.client.BalanceResponse; 035import org.apache.hadoop.hbase.client.ColumnFamilyDescriptorBuilder; 036import org.apache.hadoop.hbase.client.RegionInfo; 037import org.apache.hadoop.hbase.client.TableDescriptor; 038import org.apache.hadoop.hbase.client.TableDescriptorBuilder; 039import org.apache.hadoop.hbase.master.HMaster; 040import org.apache.hadoop.hbase.testclassification.MediumTests; 041import org.apache.hadoop.hbase.util.Bytes; 042import org.junit.After; 043import org.junit.AfterClass; 044import org.junit.Before; 045import org.junit.BeforeClass; 046import org.junit.ClassRule; 047import org.junit.Test; 048import org.junit.experimental.categories.Category; 049import org.slf4j.Logger; 050import org.slf4j.LoggerFactory; 051 052import org.apache.hbase.thirdparty.com.google.common.collect.Sets; 053 054@Category({ MediumTests.class }) 055public class TestRSGroupsBalance extends TestRSGroupsBase { 056 057 @ClassRule 058 public static final HBaseClassTestRule CLASS_RULE = 059 HBaseClassTestRule.forClass(TestRSGroupsBalance.class); 060 061 protected static final Logger LOG = LoggerFactory.getLogger(TestRSGroupsBalance.class); 062 063 @BeforeClass 064 public static void setUp() throws Exception { 065 setUpTestBeforeClass(); 066 } 067 068 @AfterClass 069 public static void tearDown() throws Exception { 070 tearDownAfterClass(); 071 } 072 073 @Before 074 public void beforeMethod() throws Exception { 075 setUpBeforeMethod(); 076 } 077 078 @After 079 public void afterMethod() throws Exception { 080 tearDownAfterMethod(); 081 } 082 083 @Test 084 public void testGroupBalance() throws Exception { 085 String methodName = name.getMethodName(); 086 087 LOG.info(methodName); 088 String newGroupName = getGroupName(methodName); 089 TableName tableName = TableName.valueOf(tablePrefix + "_ns", methodName); 090 091 ServerName first = setupBalanceTest(newGroupName, tableName); 092 093 // balance the other group and make sure it doesn't affect the new group 094 admin.balancerSwitch(true, true); 095 rsGroupAdmin.balanceRSGroup(RSGroupInfo.DEFAULT_GROUP); 096 assertEquals(6, getTableServerRegionMap().get(tableName).get(first).size()); 097 098 // disable balance, balancer will not be run and return false 099 admin.balancerSwitch(false, true); 100 assertFalse(rsGroupAdmin.balanceRSGroup(newGroupName).isBalancerRan()); 101 assertEquals(6, getTableServerRegionMap().get(tableName).get(first).size()); 102 103 // enable balance 104 admin.balancerSwitch(true, true); 105 rsGroupAdmin.balanceRSGroup(newGroupName); 106 TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() { 107 @Override 108 public boolean evaluate() throws Exception { 109 for (List<String> regions : getTableServerRegionMap().get(tableName).values()) { 110 if (2 != regions.size()) { 111 return false; 112 } 113 } 114 return true; 115 } 116 }); 117 admin.balancerSwitch(false, true); 118 } 119 120 @Test 121 public void testGroupDryRunBalance() throws Exception { 122 String methodName = name.getMethodName(); 123 124 LOG.info(methodName); 125 String newGroupName = getGroupName(methodName); 126 final TableName tableName = TableName.valueOf(tablePrefix + "_ns", methodName); 127 128 ServerName first = setupBalanceTest(newGroupName, tableName); 129 130 // run the balancer in dry run mode. it should return true, but should not actually move any 131 // regions 132 admin.balancerSwitch(true, true); 133 BalanceResponse response = rsGroupAdmin.balanceRSGroup(newGroupName, 134 BalanceRequest.newBuilder().setDryRun(true).build()); 135 assertTrue(response.isBalancerRan()); 136 assertTrue(response.getMovesCalculated() > 0); 137 assertEquals(0, response.getMovesExecuted()); 138 // validate imbalance still exists. 139 assertEquals(6, getTableServerRegionMap().get(tableName).get(first).size()); 140 } 141 142 private ServerName setupBalanceTest(String newGroupName, TableName tableName) throws Exception { 143 addGroup(newGroupName, 3); 144 145 admin.createNamespace(NamespaceDescriptor.create(tableName.getNamespaceAsString()) 146 .addConfiguration(RSGroupInfo.NAMESPACE_DESC_PROP_GROUP, newGroupName).build()); 147 final TableDescriptor desc = TableDescriptorBuilder.newBuilder(tableName) 148 .setColumnFamily(ColumnFamilyDescriptorBuilder.of("f")).build(); 149 byte[] startKey = Bytes.toBytes("aaaaa"); 150 byte[] endKey = Bytes.toBytes("zzzzz"); 151 admin.createTable(desc, startKey, endKey, 6); 152 TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() { 153 @Override 154 public boolean evaluate() throws Exception { 155 List<String> regions = getTableRegionMap().get(tableName); 156 if (regions == null) { 157 return false; 158 } 159 return regions.size() >= 6; 160 } 161 }); 162 163 // make assignment uneven, move all regions to one server 164 Map<ServerName, List<String>> assignMap = getTableServerRegionMap().get(tableName); 165 final ServerName first = assignMap.entrySet().iterator().next().getKey(); 166 for (RegionInfo region : admin.getRegions(tableName)) { 167 if (!assignMap.get(first).contains(region.getRegionNameAsString())) { 168 admin.move(region.getEncodedNameAsBytes(), first); 169 } 170 } 171 TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() { 172 @Override 173 public boolean evaluate() throws Exception { 174 Map<ServerName, List<String>> map = getTableServerRegionMap().get(tableName); 175 if (map == null) { 176 return true; 177 } 178 List<String> regions = map.get(first); 179 if (regions == null) { 180 return true; 181 } 182 return regions.size() >= 6; 183 } 184 }); 185 186 return first; 187 } 188 189 @Test 190 public void testMisplacedRegions() throws Exception { 191 final TableName tableName = TableName.valueOf(tablePrefix + "_testMisplacedRegions"); 192 LOG.info("testMisplacedRegions"); 193 194 final RSGroupInfo RSGroupInfo = addGroup("testMisplacedRegions", 1); 195 196 TEST_UTIL.createMultiRegionTable(tableName, new byte[] { 'f' }, 15); 197 TEST_UTIL.waitUntilAllRegionsAssigned(tableName); 198 199 rsGroupAdminEndpoint.getGroupInfoManager().moveTables(Sets.newHashSet(tableName), 200 RSGroupInfo.getName()); 201 202 admin.balancerSwitch(true, true); 203 assertTrue(rsGroupAdmin.balanceRSGroup(RSGroupInfo.getName()).isBalancerRan()); 204 admin.balancerSwitch(false, true); 205 assertTrue(observer.preBalanceRSGroupCalled); 206 assertTrue(observer.postBalanceRSGroupCalled); 207 208 TEST_UTIL.waitFor(60000, new Predicate<Exception>() { 209 @Override 210 public boolean evaluate() throws Exception { 211 ServerName serverName = 212 ServerName.valueOf(RSGroupInfo.getServers().iterator().next().toString(), 1); 213 return admin.getConnection().getAdmin().getRegions(serverName).size() == 15; 214 } 215 }); 216 } 217 218 @Test 219 public void testGetRSGroupAssignmentsByTable() throws Exception { 220 final TableName tableName = TableName.valueOf(name.getMethodName()); 221 TEST_UTIL.createMultiRegionTable(tableName, HConstants.CATALOG_FAMILY, 10); 222 // disable table 223 final TableName disableTableName = TableName.valueOf("testDisableTable"); 224 TEST_UTIL.createMultiRegionTable(disableTableName, HConstants.CATALOG_FAMILY, 10); 225 TEST_UTIL.getAdmin().disableTable(disableTableName); 226 227 HMaster master = TEST_UTIL.getMiniHBaseCluster().getMaster(); 228 Map<TableName, Map<ServerName, List<RegionInfo>>> assignments = 229 rsGroupAdminEndpoint.getGroupAdminServer() 230 .getRSGroupAssignmentsByTable(master.getTableStateManager(), RSGroupInfo.DEFAULT_GROUP); 231 assertFalse(assignments.containsKey(disableTableName)); 232 assertTrue(assignments.containsKey(tableName)); 233 } 234}