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