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.Assert.assertEquals; 021import static org.junit.Assert.assertFalse; 022 023import java.io.File; 024import java.io.FileWriter; 025import org.apache.hadoop.conf.Configuration; 026import org.apache.hadoop.fs.Path; 027import org.apache.hadoop.hbase.HBaseClassTestRule; 028import org.apache.hadoop.hbase.HBaseTestingUtility; 029import org.apache.hadoop.hbase.HConstants; 030import org.apache.hadoop.hbase.MiniHBaseCluster; 031import org.apache.hadoop.hbase.TableName; 032import org.apache.hadoop.hbase.Waiter.Predicate; 033import org.apache.hadoop.hbase.client.Admin; 034import org.apache.hadoop.hbase.client.ColumnFamilyDescriptorBuilder; 035import org.apache.hadoop.hbase.client.TableDescriptor; 036import org.apache.hadoop.hbase.client.TableDescriptorBuilder; 037import org.apache.hadoop.hbase.regionserver.HRegionServer; 038import org.apache.hadoop.hbase.testclassification.MediumTests; 039import org.apache.hadoop.hbase.testclassification.MiscTests; 040import org.apache.hadoop.hbase.util.RegionMover.RegionMoverBuilder; 041import org.junit.AfterClass; 042import org.junit.Before; 043import org.junit.BeforeClass; 044import org.junit.ClassRule; 045import org.junit.Test; 046import org.junit.experimental.categories.Category; 047import org.slf4j.Logger; 048import org.slf4j.LoggerFactory; 049 050/** 051 * Tests for Region Mover Load/Unload functionality with and without ack mode and also to test 052 * exclude functionality useful for rack decommissioning 053 */ 054@Category({ MiscTests.class, MediumTests.class }) 055public class TestRegionMover { 056 057 @ClassRule 058 public static final HBaseClassTestRule CLASS_RULE = 059 HBaseClassTestRule.forClass(TestRegionMover.class); 060 061 private static final Logger LOG = LoggerFactory.getLogger(TestRegionMover.class); 062 063 private static final HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility(); 064 065 @BeforeClass 066 public static void setUpBeforeClass() throws Exception { 067 TEST_UTIL.startMiniCluster(3); 068 TEST_UTIL.getAdmin().balancerSwitch(false, true); 069 } 070 071 @AfterClass 072 public static void tearDownAfterClass() throws Exception { 073 TEST_UTIL.shutdownMiniCluster(); 074 } 075 076 @Before 077 public void setUp() throws Exception { 078 // Create a pre-split table just to populate some regions 079 TableName tableName = TableName.valueOf("testRegionMover"); 080 Admin admin = TEST_UTIL.getAdmin(); 081 if (admin.tableExists(tableName)) { 082 TEST_UTIL.deleteTable(tableName); 083 } 084 TableDescriptor tableDesc = TableDescriptorBuilder.newBuilder(tableName) 085 .setColumnFamily(ColumnFamilyDescriptorBuilder.of("fam1")).build(); 086 String startKey = "a"; 087 String endKey = "z"; 088 admin.createTable(tableDesc, startKey.getBytes(), endKey.getBytes(), 9); 089 } 090 091 @Test 092 public void testWithAck() throws Exception { 093 MiniHBaseCluster cluster = TEST_UTIL.getHBaseCluster(); 094 HRegionServer regionServer = cluster.getRegionServer(0); 095 String rsName = regionServer.getServerName().getAddress().toString(); 096 int numRegions = regionServer.getNumberOfOnlineRegions(); 097 RegionMoverBuilder rmBuilder = 098 new RegionMoverBuilder(rsName, TEST_UTIL.getConfiguration()).ack(true).maxthreads(8); 099 try (RegionMover rm = rmBuilder.build()) { 100 LOG.info("Unloading " + regionServer.getServerName()); 101 rm.unload(); 102 assertEquals(0, regionServer.getNumberOfOnlineRegions()); 103 LOG.info("Successfully Unloaded\nNow Loading"); 104 rm.load(); 105 assertEquals(numRegions, regionServer.getNumberOfOnlineRegions()); 106 // Repeat the same load. It should be very fast because all regions are already moved. 107 rm.load(); 108 } 109 } 110 111 /** 112 * Test to unload a regionserver first and then load it using no Ack mode. 113 */ 114 @Test 115 public void testWithoutAck() throws Exception { 116 MiniHBaseCluster cluster = TEST_UTIL.getHBaseCluster(); 117 HRegionServer regionServer = cluster.getRegionServer(0); 118 String rsName = regionServer.getServerName().getAddress().toString(); 119 int numRegions = regionServer.getNumberOfOnlineRegions(); 120 RegionMoverBuilder rmBuilder = 121 new RegionMoverBuilder(rsName, TEST_UTIL.getConfiguration()).ack(false); 122 try (RegionMover rm = rmBuilder.build()) { 123 LOG.info("Unloading " + regionServer.getServerName()); 124 rm.unload(); 125 TEST_UTIL.waitFor(30000, 1000, new Predicate<Exception>() { 126 @Override 127 public boolean evaluate() throws Exception { 128 return regionServer.getNumberOfOnlineRegions() == 0; 129 } 130 }); 131 LOG.info("Successfully Unloaded\nNow Loading"); 132 rm.load(); 133 // In UT we only have 10 regions so it is not likely to fail, so here we check for all 134 // regions, in the real production this may not be true. 135 TEST_UTIL.waitFor(30000, 1000, new Predicate<Exception>() { 136 @Override 137 public boolean evaluate() throws Exception { 138 return regionServer.getNumberOfOnlineRegions() == numRegions; 139 } 140 }); 141 } 142 } 143 144 /** 145 * To test that we successfully exclude a server from the unloading process We test for the number 146 * of regions on Excluded server and also test that regions are unloaded successfully 147 */ 148 @Test 149 public void testExclude() throws Exception { 150 MiniHBaseCluster cluster = TEST_UTIL.getHBaseCluster(); 151 File excludeFile = new File(TEST_UTIL.getDataTestDir().toUri().getPath(), "exclude_file"); 152 FileWriter fos = new FileWriter(excludeFile); 153 HRegionServer excludeServer = cluster.getRegionServer(1); 154 String excludeHostname = excludeServer.getServerName().getHostname(); 155 int excludeServerPort = excludeServer.getServerName().getPort(); 156 int regionsExcludeServer = excludeServer.getNumberOfOnlineRegions(); 157 String excludeServerName = excludeHostname + ":" + Integer.toString(excludeServerPort); 158 fos.write(excludeServerName); 159 fos.close(); 160 HRegionServer regionServer = cluster.getRegionServer(0); 161 String rsName = regionServer.getServerName().getHostname(); 162 int port = regionServer.getServerName().getPort(); 163 String rs = rsName + ":" + Integer.toString(port); 164 RegionMoverBuilder rmBuilder = new RegionMoverBuilder(rs, TEST_UTIL.getConfiguration()) 165 .ack(true).excludeFile(excludeFile.getCanonicalPath()); 166 try (RegionMover rm = rmBuilder.build()) { 167 rm.unload(); 168 LOG.info("Unloading " + rs); 169 assertEquals(0, regionServer.getNumberOfOnlineRegions()); 170 assertEquals(regionsExcludeServer, cluster.getRegionServer(1).getNumberOfOnlineRegions()); 171 LOG.info("Before:" + regionsExcludeServer + " After:" + 172 cluster.getRegionServer(1).getNumberOfOnlineRegions()); 173 } 174 } 175 176 @Test 177 public void testRegionServerPort() { 178 MiniHBaseCluster cluster = TEST_UTIL.getHBaseCluster(); 179 HRegionServer regionServer = cluster.getRegionServer(0); 180 String rsName = regionServer.getServerName().getHostname(); 181 182 final int PORT = 16021; 183 Configuration conf = TEST_UTIL.getConfiguration(); 184 String originalPort = conf.get(HConstants.REGIONSERVER_PORT); 185 conf.set(HConstants.REGIONSERVER_PORT, Integer.toString(PORT)); 186 RegionMoverBuilder rmBuilder = new RegionMoverBuilder(rsName, conf); 187 assertEquals(PORT, rmBuilder.port); 188 if (originalPort != null) { 189 conf.set(HConstants.REGIONSERVER_PORT, originalPort); 190 } 191 } 192 193 /** 194 * UT for HBASE-21746 195 */ 196 @Test 197 public void testLoadMetaRegion() throws Exception { 198 HRegionServer rsWithMeta = TEST_UTIL.getMiniHBaseCluster().getRegionServerThreads().stream() 199 .map(t -> t.getRegionServer()) 200 .filter(rs -> rs.getRegions(TableName.META_TABLE_NAME).size() > 0).findFirst().get(); 201 int onlineRegions = rsWithMeta.getNumberOfOnlineRegions(); 202 String rsName = rsWithMeta.getServerName().getAddress().toString(); 203 try (RegionMover rm = 204 new RegionMoverBuilder(rsName, TEST_UTIL.getConfiguration()).ack(true).build()) { 205 LOG.info("Unloading " + rsWithMeta.getServerName()); 206 rm.unload(); 207 assertEquals(0, rsWithMeta.getNumberOfOnlineRegions()); 208 LOG.info("Loading " + rsWithMeta.getServerName()); 209 rm.load(); 210 assertEquals(onlineRegions, rsWithMeta.getNumberOfOnlineRegions()); 211 } 212 } 213 214 /** 215 * UT for HBASE-21746 216 */ 217 @Test 218 public void testTargetServerDeadWhenLoading() throws Exception { 219 HRegionServer rs = TEST_UTIL.getMiniHBaseCluster().getRegionServer(0); 220 String rsName = rs.getServerName().getAddress().toString(); 221 Configuration conf = new Configuration(TEST_UTIL.getConfiguration()); 222 // wait 5 seconds at most 223 conf.setInt(RegionMover.SERVERSTART_WAIT_MAX_KEY, 5); 224 String filename = 225 new Path(TEST_UTIL.getDataTestDir(), "testTargetServerDeadWhenLoading").toString(); 226 // unload the region server 227 try (RegionMover rm = 228 new RegionMoverBuilder(rsName, conf).filename(filename).ack(true).build()) { 229 LOG.info("Unloading " + rs.getServerName()); 230 rm.unload(); 231 assertEquals(0, rs.getNumberOfOnlineRegions()); 232 } 233 String inexistRsName = "whatever:123"; 234 try (RegionMover rm = 235 new RegionMoverBuilder(inexistRsName, conf).filename(filename).ack(true).build()) { 236 // load the regions to an inexist region server, which should fail and return false 237 LOG.info("Loading to an inexist region server {}", inexistRsName); 238 assertFalse(rm.load()); 239 } 240 } 241}