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.testclassification.RSGroupTests; 042import org.apache.hadoop.hbase.util.Bytes; 043import org.junit.After; 044import org.junit.AfterClass; 045import org.junit.Before; 046import org.junit.BeforeClass; 047import org.junit.ClassRule; 048import org.junit.Test; 049import org.junit.experimental.categories.Category; 050import org.slf4j.Logger; 051import org.slf4j.LoggerFactory; 052 053@Category({ RSGroupTests.class, MediumTests.class }) 054public class TestRSGroupsBalance extends TestRSGroupsBase { 055 056 @ClassRule 057 public static final HBaseClassTestRule CLASS_RULE = 058 HBaseClassTestRule.forClass(TestRSGroupsBalance.class); 059 060 protected static final Logger LOG = LoggerFactory.getLogger(TestRSGroupsBalance.class); 061 062 @BeforeClass 063 public static void setUp() throws Exception { 064 setUpTestBeforeClass(); 065 } 066 067 @AfterClass 068 public static void tearDown() throws Exception { 069 tearDownAfterClass(); 070 } 071 072 @Before 073 public void beforeMethod() throws Exception { 074 setUpBeforeMethod(); 075 } 076 077 @After 078 public void afterMethod() throws Exception { 079 tearDownAfterMethod(); 080 } 081 082 @Test 083 public void testGroupBalance() throws Exception { 084 String methodName = name.getMethodName(); 085 086 LOG.info(methodName); 087 String newGroupName = getGroupName(methodName); 088 TableName tableName = TableName.valueOf(TABLE_PREFIX + "_ns", methodName); 089 090 ServerName first = setupBalanceTest(newGroupName, tableName); 091 092 // balance the other group and make sure it doesn't affect the new group 093 ADMIN.balancerSwitch(true, true); 094 ADMIN.balanceRSGroup(RSGroupInfo.DEFAULT_GROUP); 095 assertEquals(6, getTableServerRegionMap().get(tableName).get(first).size()); 096 097 // disable balance, balancer will not be run and return false 098 ADMIN.balancerSwitch(false, true); 099 assertFalse(ADMIN.balanceRSGroup(newGroupName).isBalancerRan()); 100 assertEquals(6, getTableServerRegionMap().get(tableName).get(first).size()); 101 102 // enable balance 103 ADMIN.balancerSwitch(true, true); 104 ADMIN.balanceRSGroup(newGroupName); 105 TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() { 106 @Override 107 public boolean evaluate() throws Exception { 108 for (List<String> regions : getTableServerRegionMap().get(tableName).values()) { 109 if (2 != regions.size()) { 110 return false; 111 } 112 } 113 return true; 114 } 115 }); 116 ADMIN.balancerSwitch(false, true); 117 } 118 119 @Test 120 public void testGroupDryRunBalance() throws Exception { 121 String methodName = name.getMethodName(); 122 123 LOG.info(methodName); 124 String newGroupName = getGroupName(methodName); 125 final TableName tableName = TableName.valueOf(TABLE_PREFIX + "_ns", methodName); 126 127 ServerName first = setupBalanceTest(newGroupName, tableName); 128 129 // run the balancer in dry run mode. it should return true, but should not actually move any 130 // regions 131 ADMIN.balancerSwitch(true, true); 132 BalanceResponse response = 133 ADMIN.balanceRSGroup(newGroupName, BalanceRequest.newBuilder().setDryRun(true).build()); 134 assertTrue(response.isBalancerRan()); 135 assertTrue(response.getMovesCalculated() > 0); 136 assertEquals(0, response.getMovesExecuted()); 137 // validate imbalance still exists. 138 assertEquals(6, getTableServerRegionMap().get(tableName).get(first).size()); 139 } 140 141 private ServerName setupBalanceTest(String newGroupName, TableName tableName) throws Exception { 142 addGroup(newGroupName, 3); 143 144 ADMIN.createNamespace(NamespaceDescriptor.create(tableName.getNamespaceAsString()) 145 .addConfiguration(RSGroupInfo.NAMESPACE_DESC_PROP_GROUP, newGroupName).build()); 146 final TableDescriptor desc = TableDescriptorBuilder.newBuilder(tableName) 147 .setColumnFamily(ColumnFamilyDescriptorBuilder.of("f")).build(); 148 byte[] startKey = Bytes.toBytes("aaaaa"); 149 byte[] endKey = Bytes.toBytes("zzzzz"); 150 ADMIN.createTable(desc, startKey, endKey, 6); 151 TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() { 152 @Override 153 public boolean evaluate() throws Exception { 154 List<String> regions = getTableRegionMap().get(tableName); 155 if (regions == null) { 156 return false; 157 } 158 return regions.size() >= 6; 159 } 160 }); 161 162 // make assignment uneven, move all regions to one server 163 Map<ServerName, List<String>> assignMap = getTableServerRegionMap().get(tableName); 164 final ServerName first = assignMap.entrySet().iterator().next().getKey(); 165 for (RegionInfo region : ADMIN.getRegions(tableName)) { 166 if (!assignMap.get(first).contains(region.getRegionNameAsString())) { 167 ADMIN.move(region.getEncodedNameAsBytes(), first); 168 } 169 } 170 TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() { 171 @Override 172 public boolean evaluate() throws Exception { 173 Map<ServerName, List<String>> map = getTableServerRegionMap().get(tableName); 174 if (map == null) { 175 return true; 176 } 177 List<String> regions = map.get(first); 178 if (regions == null) { 179 return true; 180 } 181 return regions.size() >= 6; 182 } 183 }); 184 185 return first; 186 } 187 188 @Test 189 public void testMisplacedRegions() throws Exception { 190 String namespace = TABLE_PREFIX + "_" + getNameWithoutIndex(name.getMethodName()); 191 TEST_UTIL.getAdmin().createNamespace(NamespaceDescriptor.create(namespace).build()); 192 final TableName tableName = 193 TableName.valueOf(namespace, TABLE_PREFIX + "_" + getNameWithoutIndex(name.getMethodName())); 194 195 final RSGroupInfo rsGroupInfo = addGroup(getGroupName(name.getMethodName()), 1); 196 197 TEST_UTIL.createMultiRegionTable(tableName, new byte[] { 'f' }, 15); 198 TEST_UTIL.waitUntilAllRegionsAssigned(tableName); 199 TEST_UTIL.getAdmin().modifyNamespace(NamespaceDescriptor.create(namespace) 200 .addConfiguration(RSGroupInfo.NAMESPACE_DESC_PROP_GROUP, rsGroupInfo.getName()).build()); 201 202 ADMIN.balancerSwitch(true, true); 203 assertTrue(ADMIN.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 RSGroupInfoManagerImpl gm = (RSGroupInfoManagerImpl) master.getRSGroupInfoManager(); 229 Map<TableName, Map<ServerName, List<RegionInfo>>> assignments = 230 gm.getRSGroupAssignmentsByTable(master.getTableStateManager(), RSGroupInfo.DEFAULT_GROUP); 231 assertFalse(assignments.containsKey(disableTableName)); 232 assertTrue(assignments.containsKey(tableName)); 233 } 234}