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; 022 023import java.io.File; 024import java.io.FileWriter; 025import java.io.IOException; 026import java.util.Collections; 027import java.util.List; 028import org.apache.hadoop.conf.Configuration; 029import org.apache.hadoop.fs.Path; 030import org.apache.hadoop.hbase.HBaseTestingUtil; 031import org.apache.hadoop.hbase.HConstants; 032import org.apache.hadoop.hbase.ServerName; 033import org.apache.hadoop.hbase.SingleProcessHBaseCluster; 034import org.apache.hadoop.hbase.TableName; 035import org.apache.hadoop.hbase.Waiter.Predicate; 036import org.apache.hadoop.hbase.client.ColumnFamilyDescriptorBuilder; 037import org.apache.hadoop.hbase.client.TableDescriptor; 038import org.apache.hadoop.hbase.client.TableDescriptorBuilder; 039import org.apache.hadoop.hbase.regionserver.HRegion; 040import org.apache.hadoop.hbase.regionserver.HRegionServer; 041import org.apache.hadoop.hbase.testclassification.LargeTests; 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.Assertions; 047import org.junit.jupiter.api.BeforeAll; 048import org.junit.jupiter.api.BeforeEach; 049import org.junit.jupiter.api.Tag; 050import org.junit.jupiter.api.Test; 051import org.junit.jupiter.api.TestInfo; 052import org.slf4j.Logger; 053import org.slf4j.LoggerFactory; 054 055/** 056 * Tests for Region Mover Load/Unload functionality with and without ack mode and also to test 057 * exclude functionality useful for rack decommissioning 058 */ 059@Tag(MiscTests.TAG) 060@Tag(LargeTests.TAG) 061public class TestRegionMover1 { 062 063 private String testMethodName; 064 065 private static final Logger LOG = LoggerFactory.getLogger(TestRegionMover1.class); 066 067 private static final HBaseTestingUtil TEST_UTIL = new HBaseTestingUtil(); 068 069 @BeforeAll 070 public static void setUpBeforeClass() throws Exception { 071 TEST_UTIL.startMiniCluster(3); 072 TEST_UTIL.getAdmin().balancerSwitch(false, true); 073 } 074 075 @AfterAll 076 public static void tearDownAfterClass() throws Exception { 077 TEST_UTIL.shutdownMiniCluster(); 078 } 079 080 @BeforeEach 081 public void setUp(TestInfo testInfo) throws Exception { 082 testMethodName = testInfo.getTestMethod().get().getName(); 083 final TableName tableName = TableName.valueOf(testMethodName); 084 TableDescriptor tableDesc = TableDescriptorBuilder.newBuilder(tableName) 085 .setColumnFamily(ColumnFamilyDescriptorBuilder.of("fam1")).build(); 086 String startKey = "a"; 087 String endKey = "z"; 088 TEST_UTIL.getAdmin().createTable(tableDesc, Bytes.toBytes(startKey), Bytes.toBytes(endKey), 9); 089 } 090 091 @AfterEach 092 public void tearDown() throws Exception { 093 final TableName tableName = TableName.valueOf(testMethodName); 094 TEST_UTIL.getAdmin().disableTable(tableName); 095 TEST_UTIL.getAdmin().deleteTable(tableName); 096 } 097 098 @Test 099 public void testWithAck() throws Exception { 100 SingleProcessHBaseCluster cluster = TEST_UTIL.getHBaseCluster(); 101 HRegionServer regionServer = cluster.getRegionServer(0); 102 String rsName = regionServer.getServerName().getAddress().toString(); 103 int numRegions = regionServer.getNumberOfOnlineRegions(); 104 RegionMoverBuilder rmBuilder = 105 new RegionMoverBuilder(rsName, TEST_UTIL.getConfiguration()).ack(true).maxthreads(8); 106 try (RegionMover rm = rmBuilder.build()) { 107 LOG.info("Unloading " + regionServer.getServerName()); 108 rm.unload(); 109 assertEquals(0, regionServer.getNumberOfOnlineRegions()); 110 LOG.info("Successfully Unloaded\nNow Loading"); 111 rm.load(); 112 assertEquals(numRegions, regionServer.getNumberOfOnlineRegions()); 113 // Repeat the same load. It should be very fast because all regions are already moved. 114 rm.load(); 115 } 116 } 117 118 /** 119 * Test to unload a regionserver first and then load it using no Ack mode. 120 */ 121 @Test 122 public void testWithoutAck() throws Exception { 123 SingleProcessHBaseCluster cluster = TEST_UTIL.getHBaseCluster(); 124 HRegionServer regionServer = cluster.getRegionServer(0); 125 String rsName = regionServer.getServerName().getAddress().toString(); 126 int numRegions = regionServer.getNumberOfOnlineRegions(); 127 RegionMoverBuilder rmBuilder = 128 new RegionMoverBuilder(rsName, TEST_UTIL.getConfiguration()).ack(false); 129 try (RegionMover rm = rmBuilder.build()) { 130 LOG.info("Unloading " + regionServer.getServerName()); 131 rm.unload(); 132 TEST_UTIL.waitFor(30000, 1000, new Predicate<Exception>() { 133 @Override 134 public boolean evaluate() throws Exception { 135 return regionServer.getNumberOfOnlineRegions() == 0; 136 } 137 }); 138 LOG.info("Successfully Unloaded\nNow Loading"); 139 rm.load(); 140 // In UT we only have 10 regions so it is not likely to fail, so here we check for all 141 // regions, in the real production this may not be true. 142 TEST_UTIL.waitFor(30000, 1000, new Predicate<Exception>() { 143 @Override 144 public boolean evaluate() throws Exception { 145 return regionServer.getNumberOfOnlineRegions() == numRegions; 146 } 147 }); 148 } 149 } 150 151 /** 152 * To test that we successfully exclude a server from the unloading process We test for the number 153 * of regions on Excluded server and also test that regions are unloaded successfully 154 */ 155 @Test 156 public void testExclude() throws Exception { 157 SingleProcessHBaseCluster cluster = TEST_UTIL.getHBaseCluster(); 158 File excludeFile = new File(TEST_UTIL.getDataTestDir().toUri().getPath(), "exclude_file"); 159 FileWriter fos = new FileWriter(excludeFile); 160 HRegionServer excludeServer = cluster.getRegionServer(1); 161 String excludeHostname = excludeServer.getServerName().getHostname(); 162 int excludeServerPort = excludeServer.getServerName().getPort(); 163 int regionsExcludeServer = excludeServer.getNumberOfOnlineRegions(); 164 String excludeServerName = excludeHostname + ":" + Integer.toString(excludeServerPort); 165 fos.write(excludeServerName); 166 fos.close(); 167 HRegionServer regionServer = cluster.getRegionServer(0); 168 String rsName = regionServer.getServerName().getHostname(); 169 int port = regionServer.getServerName().getPort(); 170 String rs = rsName + ":" + Integer.toString(port); 171 RegionMoverBuilder rmBuilder = new RegionMoverBuilder(rs, TEST_UTIL.getConfiguration()) 172 .ack(true).excludeFile(excludeFile.getCanonicalPath()); 173 try (RegionMover rm = rmBuilder.build()) { 174 rm.unload(); 175 LOG.info("Unloading " + rs); 176 assertEquals(0, regionServer.getNumberOfOnlineRegions()); 177 assertEquals(regionsExcludeServer, cluster.getRegionServer(1).getNumberOfOnlineRegions()); 178 LOG.info("Before:" + regionsExcludeServer + " After:" 179 + cluster.getRegionServer(1).getNumberOfOnlineRegions()); 180 } 181 } 182 183 @Test 184 public void testDesignatedFile() throws Exception { 185 SingleProcessHBaseCluster cluster = TEST_UTIL.getHBaseCluster(); 186 File designatedFile = new File(TEST_UTIL.getDataTestDir().toUri().getPath(), "designated_file"); 187 HRegionServer designatedServer = cluster.getRegionServer(0); 188 try (FileWriter fos = new FileWriter(designatedFile)) { 189 String designatedHostname = designatedServer.getServerName().getHostname(); 190 int designatedServerPort = designatedServer.getServerName().getPort(); 191 String excludeServerName = designatedHostname + ":" + designatedServerPort; 192 fos.write(excludeServerName); 193 } 194 int regionsInDesignatedServer = designatedServer.getNumberOfOnlineRegions(); 195 HRegionServer regionServer = cluster.getRegionServer(1); 196 String rsName = regionServer.getServerName().getHostname(); 197 int port = regionServer.getServerName().getPort(); 198 String rs = rsName + ":" + port; 199 int regionsInRegionServer = regionServer.getNumberOfOnlineRegions(); 200 RegionMoverBuilder rmBuilder = new RegionMoverBuilder(rs, TEST_UTIL.getConfiguration()) 201 .designatedFile(designatedFile.getCanonicalPath()); 202 try (RegionMover rm = rmBuilder.build()) { 203 LOG.debug("Unloading {} regions", rs); 204 rm.unload(); 205 assertEquals(0, regionServer.getNumberOfOnlineRegions()); 206 assertEquals(regionsInDesignatedServer + regionsInRegionServer, 207 designatedServer.getNumberOfOnlineRegions()); 208 LOG.debug("Before:{} After:{}", regionsInDesignatedServer, 209 designatedServer.getNumberOfOnlineRegions()); 210 } 211 } 212 213 @Test 214 public void testExcludeAndDesignated() throws Exception { 215 SingleProcessHBaseCluster cluster = TEST_UTIL.getHBaseCluster(); 216 // create designated file 217 File designatedFile = new File(TEST_UTIL.getDataTestDir().toUri().getPath(), "designated_file"); 218 HRegionServer designatedServer = cluster.getRegionServer(0); 219 try (FileWriter fos = new FileWriter(designatedFile)) { 220 String designatedHostname = designatedServer.getServerName().getHostname(); 221 int designatedServerPort = designatedServer.getServerName().getPort(); 222 String excludeServerName = designatedHostname + ":" + designatedServerPort; 223 fos.write(excludeServerName); 224 } 225 int regionsInDesignatedServer = designatedServer.getNumberOfOnlineRegions(); 226 // create exclude file 227 File excludeFile = new File(TEST_UTIL.getDataTestDir().toUri().getPath(), "exclude_file"); 228 HRegionServer excludeServer = cluster.getRegionServer(1); 229 try (FileWriter fos = new FileWriter(excludeFile)) { 230 String excludeHostname = excludeServer.getServerName().getHostname(); 231 int excludeServerPort = excludeServer.getServerName().getPort(); 232 String excludeServerName = excludeHostname + ":" + excludeServerPort; 233 fos.write(excludeServerName); 234 } 235 int regionsInExcludeServer = excludeServer.getNumberOfOnlineRegions(); 236 237 HRegionServer targetRegionServer = cluster.getRegionServer(2); 238 String rsName = targetRegionServer.getServerName().getHostname(); 239 int port = targetRegionServer.getServerName().getPort(); 240 String rs = rsName + ":" + port; 241 int regionsInTargetRegionServer = targetRegionServer.getNumberOfOnlineRegions(); 242 243 RegionMoverBuilder rmBuilder = new RegionMoverBuilder(rs, TEST_UTIL.getConfiguration()) 244 .designatedFile(designatedFile.getCanonicalPath()) 245 .excludeFile(excludeFile.getCanonicalPath()); 246 try (RegionMover rm = rmBuilder.build()) { 247 LOG.debug("Unloading {}", rs); 248 rm.unload(); 249 assertEquals(0, targetRegionServer.getNumberOfOnlineRegions()); 250 assertEquals(regionsInDesignatedServer + regionsInTargetRegionServer, 251 designatedServer.getNumberOfOnlineRegions()); 252 LOG.debug("DesignatedServer Before:{} After:{}", regionsInDesignatedServer, 253 designatedServer.getNumberOfOnlineRegions()); 254 assertEquals(regionsInExcludeServer, excludeServer.getNumberOfOnlineRegions()); 255 LOG.debug("ExcludeServer Before:{} After:{}", regionsInExcludeServer, 256 excludeServer.getNumberOfOnlineRegions()); 257 } 258 } 259 260 @Test 261 public void testRegionServerPort() throws Exception { 262 SingleProcessHBaseCluster cluster = TEST_UTIL.getHBaseCluster(); 263 HRegionServer regionServer = cluster.getRegionServer(0); 264 String rsName = regionServer.getServerName().getHostname(); 265 266 final int PORT = 16021; 267 Configuration conf = TEST_UTIL.getConfiguration(); 268 String originalPort = conf.get(HConstants.REGIONSERVER_PORT); 269 conf.set(HConstants.REGIONSERVER_PORT, Integer.toString(PORT)); 270 RegionMoverBuilder rmBuilder = new RegionMoverBuilder(rsName, conf); 271 assertEquals(PORT, rmBuilder.port); 272 if (originalPort != null) { 273 conf.set(HConstants.REGIONSERVER_PORT, originalPort); 274 } 275 } 276 277 /** 278 * UT for HBASE-21746 279 */ 280 @Test 281 public void testLoadMetaRegion() throws Exception { 282 HRegionServer rsWithMeta = TEST_UTIL.getMiniHBaseCluster().getRegionServerThreads().stream() 283 .map(t -> t.getRegionServer()) 284 .filter(rs -> rs.getRegions(TableName.META_TABLE_NAME).size() > 0).findFirst().get(); 285 int onlineRegions = rsWithMeta.getNumberOfOnlineRegions(); 286 String rsName = rsWithMeta.getServerName().getAddress().toString(); 287 try (RegionMover rm = 288 new RegionMoverBuilder(rsName, TEST_UTIL.getConfiguration()).ack(true).build()) { 289 LOG.info("Unloading " + rsWithMeta.getServerName()); 290 rm.unload(); 291 assertEquals(0, rsWithMeta.getNumberOfOnlineRegions()); 292 LOG.info("Loading " + rsWithMeta.getServerName()); 293 rm.load(); 294 assertEquals(onlineRegions, rsWithMeta.getNumberOfOnlineRegions()); 295 } 296 } 297 298 /** 299 * UT for HBASE-21746 300 */ 301 @Test 302 public void testTargetServerDeadWhenLoading() throws Exception { 303 HRegionServer rs = TEST_UTIL.getMiniHBaseCluster().getRegionServer(0); 304 String rsName = rs.getServerName().getAddress().toString(); 305 Configuration conf = new Configuration(TEST_UTIL.getConfiguration()); 306 // wait 5 seconds at most 307 conf.setInt(RegionMover.SERVERSTART_WAIT_MAX_KEY, 5); 308 String filename = 309 new Path(TEST_UTIL.getDataTestDir(), "testTargetServerDeadWhenLoading").toString(); 310 // unload the region server 311 try ( 312 RegionMover rm = new RegionMoverBuilder(rsName, conf).filename(filename).ack(true).build()) { 313 LOG.info("Unloading " + rs.getServerName()); 314 rm.unload(); 315 assertEquals(0, rs.getNumberOfOnlineRegions()); 316 } 317 String inexistRsName = "whatever:123"; 318 try (RegionMover rm = 319 new RegionMoverBuilder(inexistRsName, conf).filename(filename).ack(true).build()) { 320 // load the regions to an inexist region server, which should fail and return false 321 LOG.info("Loading to an inexist region server {}", inexistRsName); 322 assertFalse(rm.load()); 323 } 324 } 325 326 @Test 327 public void testDecomServerExclusionWithAck() throws Exception { 328 SingleProcessHBaseCluster cluster = TEST_UTIL.getHBaseCluster(); 329 HRegionServer excludeServer = cluster.getRegionServer(1); 330 List<HRegion> regions = excludeServer.getRegions(); 331 int regionsExcludeServer = excludeServer.getNumberOfOnlineRegions(); 332 TEST_UTIL.getAdmin() 333 .decommissionRegionServers(Collections.singletonList(excludeServer.getServerName()), false); 334 335 waitForServerDecom(excludeServer); 336 337 HRegionServer regionServer = cluster.getRegionServer(0); 338 String rsName = regionServer.getServerName().getHostname(); 339 int port = regionServer.getServerName().getPort(); 340 String hostname = rsName + ":" + Integer.toString(port); 341 RegionMoverBuilder rmBuilder = 342 new RegionMoverBuilder(hostname, TEST_UTIL.getConfiguration()).ack(true); 343 344 int targetServerRegions = cluster.getRegionServer(2).getRegions().size(); 345 int sourceServerRegions = regionServer.getRegions().size(); 346 347 try (RegionMover regionMover = rmBuilder.build()) { 348 Assertions.assertTrue(regionMover.unload()); 349 LOG.info("Unloading {}", hostname); 350 assertEquals(0, regionServer.getNumberOfOnlineRegions()); 351 assertEquals(regionsExcludeServer, cluster.getRegionServer(1).getNumberOfOnlineRegions()); 352 LOG.info("Before:" + regionsExcludeServer + " After:" 353 + cluster.getRegionServer(1).getNumberOfOnlineRegions()); 354 List<HRegion> regionList = cluster.getRegionServer(1).getRegions(); 355 int index = 0; 356 for (HRegion hRegion : regionList) { 357 Assertions.assertEquals(hRegion, regions.get(index++)); 358 } 359 Assertions.assertEquals(targetServerRegions + sourceServerRegions, 360 cluster.getRegionServer(2).getNumberOfOnlineRegions()); 361 Assertions.assertTrue(regionMover.load()); 362 } 363 364 TEST_UTIL.getAdmin().recommissionRegionServer(excludeServer.getServerName(), 365 Collections.emptyList()); 366 } 367 368 private void waitForServerDecom(HRegionServer excludeServer) { 369 370 TEST_UTIL.waitFor(3000, () -> { 371 try { 372 List<ServerName> decomServers = TEST_UTIL.getAdmin().listDecommissionedRegionServers(); 373 return decomServers.size() == 1 374 && decomServers.get(0).equals(excludeServer.getServerName()); 375 } catch (IOException e) { 376 throw new RuntimeException(e); 377 } 378 }); 379 } 380 381 @Test 382 public void testDecomServerExclusion() throws Exception { 383 SingleProcessHBaseCluster cluster = TEST_UTIL.getHBaseCluster(); 384 HRegionServer excludeServer = cluster.getRegionServer(0); 385 List<HRegion> regions = excludeServer.getRegions(); 386 int regionsExcludeServer = excludeServer.getNumberOfOnlineRegions(); 387 TEST_UTIL.getAdmin() 388 .decommissionRegionServers(Collections.singletonList(excludeServer.getServerName()), false); 389 390 waitForServerDecom(excludeServer); 391 392 HRegionServer sourceRegionServer = cluster.getRegionServer(1); 393 String rsName = sourceRegionServer.getServerName().getHostname(); 394 int port = sourceRegionServer.getServerName().getPort(); 395 String hostname = rsName + ":" + Integer.toString(port); 396 RegionMoverBuilder rmBuilder = 397 new RegionMoverBuilder(hostname, TEST_UTIL.getConfiguration()).ack(false); 398 399 int targetServerRegions = cluster.getRegionServer(2).getRegions().size(); 400 int sourceServerRegions = sourceRegionServer.getRegions().size(); 401 402 try (RegionMover regionMover = rmBuilder.build()) { 403 Assertions.assertTrue(regionMover.unload()); 404 LOG.info("Unloading {}", hostname); 405 assertEquals(0, sourceRegionServer.getNumberOfOnlineRegions()); 406 assertEquals(regionsExcludeServer, cluster.getRegionServer(0).getNumberOfOnlineRegions()); 407 LOG.info("Before:" + regionsExcludeServer + " After:" 408 + cluster.getRegionServer(1).getNumberOfOnlineRegions()); 409 List<HRegion> regionList = cluster.getRegionServer(0).getRegions(); 410 int index = 0; 411 for (HRegion hRegion : regionList) { 412 Assertions.assertEquals(hRegion, regions.get(index++)); 413 } 414 Assertions.assertEquals(targetServerRegions + sourceServerRegions, 415 cluster.getRegionServer(2).getNumberOfOnlineRegions()); 416 Assertions.assertTrue(regionMover.load()); 417 } 418 419 TEST_UTIL.getAdmin().recommissionRegionServer(excludeServer.getServerName(), 420 Collections.emptyList()); 421 } 422 423 @Test 424 public void testExcludeAndDecomServers() throws Exception { 425 SingleProcessHBaseCluster cluster = TEST_UTIL.getHBaseCluster(); 426 File excludeFile = new File(TEST_UTIL.getDataTestDir().toUri().getPath(), "exclude_file"); 427 FileWriter fos = new FileWriter(excludeFile); 428 HRegionServer excludeServer = cluster.getRegionServer(1); 429 String excludeHostname = excludeServer.getServerName().getHostname(); 430 int excludeServerPort = excludeServer.getServerName().getPort(); 431 String excludeServerName = excludeHostname + ":" + Integer.toString(excludeServerPort); 432 fos.write(excludeServerName); 433 fos.close(); 434 435 HRegionServer decomServer = cluster.getRegionServer(2); 436 TEST_UTIL.getAdmin() 437 .decommissionRegionServers(Collections.singletonList(decomServer.getServerName()), false); 438 439 waitForServerDecom(decomServer); 440 441 HRegionServer regionServer = cluster.getRegionServer(0); 442 String rsName = regionServer.getServerName().getHostname(); 443 int port = regionServer.getServerName().getPort(); 444 String sourceServer = rsName + ":" + Integer.toString(port); 445 RegionMoverBuilder rmBuilder = 446 new RegionMoverBuilder(sourceServer, TEST_UTIL.getConfiguration()).ack(true) 447 .excludeFile(excludeFile.getCanonicalPath()); 448 try (RegionMover regionMover = rmBuilder.build()) { 449 Assertions.assertFalse(regionMover.unload()); 450 } 451 452 TEST_UTIL.getAdmin().recommissionRegionServer(decomServer.getServerName(), 453 Collections.emptyList()); 454 } 455 456}