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.util; 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.ArrayList; 025import java.util.Collection; 026import java.util.HashSet; 027import java.util.List; 028import org.apache.hadoop.fs.Path; 029import org.apache.hadoop.hbase.HBaseTestingUtil; 030import org.apache.hadoop.hbase.ServerName; 031import org.apache.hadoop.hbase.TableName; 032import org.apache.hadoop.hbase.client.Admin; 033import org.apache.hadoop.hbase.client.ColumnFamilyDescriptorBuilder; 034import org.apache.hadoop.hbase.client.TableDescriptor; 035import org.apache.hadoop.hbase.client.TableDescriptorBuilder; 036import org.apache.hadoop.hbase.net.Address; 037import org.apache.hadoop.hbase.regionserver.HRegion; 038import org.apache.hadoop.hbase.regionserver.HRegionServer; 039import org.apache.hadoop.hbase.rsgroup.RSGroupInfo; 040import org.apache.hadoop.hbase.rsgroup.RSGroupUtil; 041import org.apache.hadoop.hbase.testclassification.MediumTests; 042import org.apache.hadoop.hbase.testclassification.MiscTests; 043import org.apache.hadoop.hbase.util.RegionMover.RegionMoverBuilder; 044import org.junit.jupiter.api.AfterAll; 045import org.junit.jupiter.api.AfterEach; 046import org.junit.jupiter.api.BeforeAll; 047import org.junit.jupiter.api.BeforeEach; 048import org.junit.jupiter.api.Tag; 049import org.junit.jupiter.api.Test; 050import org.slf4j.Logger; 051import org.slf4j.LoggerFactory; 052 053/** 054 * Test for rsgroup enable, unloaded regions from decommissoned host of a rsgroup should be assigned 055 * to those regionservers belonging to the same rsgroup. 056 */ 057@Tag(MiscTests.TAG) 058@Tag(MediumTests.TAG) 059public class TestRegionMoverWithRSGroupEnable { 060 061 private static final Logger LOG = LoggerFactory.getLogger(TestRegionMoverWithRSGroupEnable.class); 062 private static final HBaseTestingUtil TEST_UTIL = new HBaseTestingUtil(); 063 private static final String TEST_RSGROUP = "test"; 064 065 @BeforeAll 066 public static void setUpBeforeClass() throws Exception { 067 RSGroupUtil.enableRSGroup(TEST_UTIL.getConfiguration()); 068 TEST_UTIL.startMiniCluster(5); 069 } 070 071 @AfterAll 072 public static void tearDownAfterClass() throws Exception { 073 TEST_UTIL.shutdownMiniCluster(); 074 } 075 076 private static final TableName TABLE_NAME = TableName.valueOf("testRegionMoverWithRSGroupEnable"); 077 078 private final List<Address> rsservers = new ArrayList<>(2); 079 private final List<ServerName> defaultGroupServers = new ArrayList<>(); 080 private ServerName rsContainMeta; 081 082 @BeforeEach 083 public void setUp() throws Exception { 084 Admin admin = TEST_UTIL.getAdmin(); 085 086 // Add a new rsgroup and assign two servers to it. 087 admin.addRSGroup(TEST_RSGROUP); 088 Collection<ServerName> allServers = admin.getRegionServers(); 089 // Remove rs contains hbase:meta, otherwise test looks unstable and buggy in test env. 090 rsContainMeta = TEST_UTIL.getMiniHBaseCluster().getRegionServerThreads().stream() 091 .map(t -> t.getRegionServer()) 092 .filter(rs -> rs.getRegions(TableName.META_TABLE_NAME).size() > 0).findFirst().get() 093 .getServerName(); 094 LOG.info("{} contains hbase:meta", rsContainMeta); 095 List<ServerName> modifiable = new ArrayList<>(allServers); 096 modifiable.remove(rsContainMeta); 097 int i = 0; 098 for (ServerName server : modifiable) { 099 if (i == 2) break; 100 rsservers.add(Address.fromParts(server.getHostname(), server.getPort())); 101 i++; 102 } 103 admin.moveServersToRSGroup(new HashSet<>(rsservers), TEST_RSGROUP); 104 LOG.info("Servers in {} are {}", TEST_RSGROUP, rsservers); 105 assertEquals(3, admin.getRSGroup(RSGroupInfo.DEFAULT_GROUP).getServers().size()); 106 assertEquals(2, admin.getRSGroup(TEST_RSGROUP).getServers().size()); 107 108 // Track the servers left in the default group, used for isolation assertions. 109 for (ServerName server : allServers) { 110 if (!rsservers.contains(Address.fromParts(server.getHostname(), server.getPort()))) { 111 defaultGroupServers.add(server); 112 } 113 } 114 115 // Create a pre-split table in test rsgroup 116 if (admin.tableExists(TABLE_NAME)) { 117 TEST_UTIL.deleteTable(TABLE_NAME); 118 } 119 TableDescriptor tableDesc = TableDescriptorBuilder.newBuilder(TABLE_NAME) 120 .setColumnFamily(ColumnFamilyDescriptorBuilder.of("f")).setRegionServerGroup(TEST_RSGROUP) 121 .build(); 122 String startKey = "a"; 123 String endKey = "z"; 124 admin.createTable(tableDesc, Bytes.toBytes(startKey), Bytes.toBytes(endKey), 9); 125 } 126 127 @AfterEach 128 public void tearDown() throws Exception { 129 Admin admin = TEST_UTIL.getAdmin(); 130 if (admin.tableExists(TABLE_NAME)) { 131 TEST_UTIL.deleteTable(TABLE_NAME); 132 } 133 if (!rsservers.isEmpty()) { 134 admin.moveServersToRSGroup(new HashSet<>(rsservers), RSGroupInfo.DEFAULT_GROUP); 135 } 136 if (admin.getRSGroup(TEST_RSGROUP) != null) { 137 admin.removeRSGroup(TEST_RSGROUP); 138 } 139 rsservers.clear(); 140 defaultGroupServers.clear(); 141 rsContainMeta = null; 142 } 143 144 @Test 145 public void testUnloadRegions() throws Exception { 146 Address decommission = rsservers.get(0); 147 Address online = rsservers.get(1); 148 String filename = new Path(TEST_UTIL.getDataTestDir(), "testRSGroupUnload").toString(); 149 RegionMoverBuilder builder = 150 new RegionMoverBuilder(decommission.toString(), TEST_UTIL.getConfiguration()); 151 try (RegionMover rm = builder.filename(filename).ack(true).build()) { 152 LOG.info("Unloading " + decommission.getHostname()); 153 rm.unload(); 154 } 155 HRegionServer onlineRS = TEST_UTIL.getMiniHBaseCluster().getRegionServerThreads().stream() 156 .map(JVMClusterUtil.RegionServerThread::getRegionServer) 157 .filter(rs -> rs.getServerName().getAddress().equals(online)).findFirst().get(); 158 assertEquals(9, onlineRS.getNumberOfOnlineRegions()); 159 160 // Isolation assertion: no default-group server must hold any region of the test table. 161 for (ServerName defaultSN : defaultGroupServers) { 162 HRegionServer defaultRS = TEST_UTIL.getMiniHBaseCluster().getRegionServerThreads().stream() 163 .map(JVMClusterUtil.RegionServerThread::getRegionServer) 164 .filter(rs -> rs.getServerName().equals(defaultSN)).findFirst().orElse(null); 165 if (defaultRS == null) { 166 continue; 167 } 168 List<HRegion> tableRegions = defaultRS.getRegions(TABLE_NAME); 169 assertTrue(tableRegions.isEmpty(), "Default-group server " + defaultSN 170 + " must not hold any regions of " + TABLE_NAME + " but had: " + tableRegions); 171 } 172 } 173 174 /** 175 * Unloading a server that is in the default RSGroup must still succeed end-to-end when RSGroups 176 * are enabled. Destinations must be filtered to the default group: regions may spread across the 177 * other default-group servers, but must not land on any test-group server. 178 */ 179 @Test 180 public void testUnloadDefaultGroupServerWithRSGroupEnabled() throws Exception { 181 Admin admin = TEST_UTIL.getAdmin(); 182 // Avoid unloading the meta-carrying server here too, for the same stability reason setUp() 183 // avoids it when picking rsservers. 184 ServerName defaultSN = 185 defaultGroupServers.stream().filter(sn -> !sn.equals(rsContainMeta)).findFirst().get(); 186 Address decommission = defaultSN.getAddress(); 187 String filename = new Path(TEST_UTIL.getDataTestDir(), "testDefaultGroupUnload").toString(); 188 189 // Create a table in the default group; the balancer will distribute its regions naturally 190 // across the default-group servers, so defaultSN will hold at least some. 191 TableName defaultTable = TableName.valueOf("testDefaultGroupTable"); 192 if (admin.tableExists(defaultTable)) { 193 TEST_UTIL.deleteTable(defaultTable); 194 } 195 try { 196 TableDescriptor td = TableDescriptorBuilder.newBuilder(defaultTable) 197 .setColumnFamily(ColumnFamilyDescriptorBuilder.of("f")).build(); 198 admin.createTable(td, Bytes.toBytes("a"), Bytes.toBytes("z"), 6); 199 TEST_UTIL.waitTableAvailable(defaultTable); 200 201 HRegionServer decommRS = TEST_UTIL.getMiniHBaseCluster().getRegionServerThreads().stream() 202 .map(JVMClusterUtil.RegionServerThread::getRegionServer) 203 .filter(rs -> rs.getServerName().equals(defaultSN)).findFirst().get(); 204 assertFalse(decommRS.getRegions(defaultTable).isEmpty(), 205 "Precondition: decommissioned server must actually host some regions of the default " 206 + "table, otherwise the post-unload check below is vacuous"); 207 208 RegionMoverBuilder builder = 209 new RegionMoverBuilder(decommission.toString(), TEST_UTIL.getConfiguration()); 210 try (RegionMover rm = builder.filename(filename).ack(true).build()) { 211 LOG.info("Unloading default-group server {}", decommission.getHostname()); 212 rm.unload(); 213 } 214 215 // After unload, the decommissioned server must hold no regions of the default table. 216 assertEquals(0, decommRS.getRegions(defaultTable).size(), 217 "Decommissioned default-group server must hold no regions after unload"); 218 219 // Isolation assertion: no test-group server must hold any region of the default table. 220 for (JVMClusterUtil.RegionServerThread rst : TEST_UTIL.getMiniHBaseCluster() 221 .getRegionServerThreads()) { 222 HRegionServer rs = rst.getRegionServer(); 223 Address addr = rs.getServerName().getAddress(); 224 if (rsservers.contains(addr)) { 225 List<HRegion> found = rs.getRegions(defaultTable); 226 assertTrue(found.isEmpty(), "Test-group server " + addr + " must not hold any region of " 227 + defaultTable + " but had: " + found); 228 } 229 } 230 } finally { 231 if (admin.tableExists(defaultTable)) { 232 TEST_UTIL.deleteTable(defaultTable); 233 } 234 } 235 } 236 237}