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.master; 019 020import static org.junit.Assert.assertArrayEquals; 021import static org.junit.Assert.assertEquals; 022import static org.junit.Assert.assertFalse; 023import static org.junit.Assert.assertTrue; 024import static org.junit.Assert.fail; 025 026import java.io.IOException; 027import java.util.List; 028import java.util.concurrent.atomic.AtomicReference; 029import org.apache.hadoop.fs.FileSystem; 030import org.apache.hadoop.fs.Path; 031import org.apache.hadoop.hbase.HBaseClassTestRule; 032import org.apache.hadoop.hbase.HBaseTestingUtility; 033import org.apache.hadoop.hbase.HColumnDescriptor; 034import org.apache.hadoop.hbase.HConstants; 035import org.apache.hadoop.hbase.HTableDescriptor; 036import org.apache.hadoop.hbase.MetaTableAccessor; 037import org.apache.hadoop.hbase.MiniHBaseCluster; 038import org.apache.hadoop.hbase.PleaseHoldException; 039import org.apache.hadoop.hbase.ServerName; 040import org.apache.hadoop.hbase.TableName; 041import org.apache.hadoop.hbase.UnknownRegionException; 042import org.apache.hadoop.hbase.client.Admin; 043import org.apache.hadoop.hbase.client.RegionInfo; 044import org.apache.hadoop.hbase.client.RegionInfoBuilder; 045import org.apache.hadoop.hbase.client.Result; 046import org.apache.hadoop.hbase.client.Table; 047import org.apache.hadoop.hbase.client.TableState; 048import org.apache.hadoop.hbase.testclassification.MasterTests; 049import org.apache.hadoop.hbase.testclassification.MediumTests; 050import org.apache.hadoop.hbase.util.Bytes; 051import org.apache.hadoop.hbase.util.HBaseFsck; 052import org.apache.hadoop.hbase.util.Pair; 053import org.apache.hadoop.util.StringUtils; 054import org.junit.AfterClass; 055import org.junit.BeforeClass; 056import org.junit.ClassRule; 057import org.junit.Rule; 058import org.junit.Test; 059import org.junit.experimental.categories.Category; 060import org.junit.rules.TestName; 061import org.slf4j.Logger; 062import org.slf4j.LoggerFactory; 063 064import org.apache.hbase.thirdparty.com.google.common.base.Joiner; 065 066@Category({ MasterTests.class, MediumTests.class }) 067public class TestMaster { 068 069 @ClassRule 070 public static final HBaseClassTestRule CLASS_RULE = HBaseClassTestRule.forClass(TestMaster.class); 071 072 private static final HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility(); 073 private static final Logger LOG = LoggerFactory.getLogger(TestMaster.class); 074 private static final TableName TABLENAME = TableName.valueOf("TestMaster"); 075 private static final byte[] FAMILYNAME = Bytes.toBytes("fam"); 076 private static Admin admin; 077 078 @Rule 079 public TestName name = new TestName(); 080 081 @BeforeClass 082 public static void beforeAllTests() throws Exception { 083 // we will retry operations when PleaseHoldException is thrown 084 TEST_UTIL.getConfiguration().setInt(HConstants.HBASE_CLIENT_RETRIES_NUMBER, 3); 085 // Set hbase.min.version.move.system.tables as version 0 so that 086 // testMoveRegionWhenNotInitialized never fails even if hbase-default has valid default 087 // value present for production use-case. 088 // See HBASE-22923 for details. 089 TEST_UTIL.getConfiguration().set("hbase.min.version.move.system.tables", "0.0.0"); 090 // Start a cluster of two regionservers. 091 TEST_UTIL.startMiniCluster(2); 092 admin = TEST_UTIL.getAdmin(); 093 } 094 095 @AfterClass 096 public static void afterAllTests() throws Exception { 097 TEST_UTIL.shutdownMiniCluster(); 098 } 099 100 /** 101 * Return the region and current deployment for the region containing the given row. If the region 102 * cannot be found, returns null. If it is found, but not currently deployed, the second element 103 * of the pair may be null. 104 */ 105 private Pair<RegionInfo, ServerName> getTableRegionForRow(HMaster master, TableName tableName, 106 byte[] rowKey) throws IOException { 107 final AtomicReference<Pair<RegionInfo, ServerName>> result = new AtomicReference<>(null); 108 109 MetaTableAccessor.Visitor visitor = new MetaTableAccessor.Visitor() { 110 @Override 111 public boolean visit(Result data) throws IOException { 112 if (data == null || data.size() <= 0) { 113 return true; 114 } 115 Pair<RegionInfo, ServerName> pair = new Pair<>(MetaTableAccessor.getRegionInfo(data), 116 MetaTableAccessor.getServerName(data, 0)); 117 if (!pair.getFirst().getTable().equals(tableName)) { 118 return false; 119 } 120 result.set(pair); 121 return true; 122 } 123 }; 124 125 MetaTableAccessor.scanMeta(master.getConnection(), visitor, tableName, rowKey, 1); 126 return result.get(); 127 } 128 129 @Test 130 @SuppressWarnings("deprecation") 131 public void testMasterOpsWhileSplitting() throws Exception { 132 MiniHBaseCluster cluster = TEST_UTIL.getHBaseCluster(); 133 HMaster m = cluster.getMaster(); 134 135 try (Table ht = TEST_UTIL.createTable(TABLENAME, FAMILYNAME)) { 136 assertTrue(m.getTableStateManager().isTableState(TABLENAME, TableState.State.ENABLED)); 137 TEST_UTIL.loadTable(ht, FAMILYNAME, false); 138 } 139 140 List<Pair<RegionInfo, ServerName>> tableRegions = 141 MetaTableAccessor.getTableRegionsAndLocations(m.getConnection(), TABLENAME); 142 LOG.info("Regions after load: " + Joiner.on(',').join(tableRegions)); 143 assertEquals(1, tableRegions.size()); 144 assertArrayEquals(HConstants.EMPTY_START_ROW, tableRegions.get(0).getFirst().getStartKey()); 145 assertArrayEquals(HConstants.EMPTY_END_ROW, tableRegions.get(0).getFirst().getEndKey()); 146 147 // Now trigger a split and stop when the split is in progress 148 LOG.info("Splitting table"); 149 TEST_UTIL.getAdmin().split(TABLENAME); 150 151 LOG.info("Making sure we can call getTableRegions while opening"); 152 while (tableRegions.size() < 3) { 153 tableRegions = 154 MetaTableAccessor.getTableRegionsAndLocations(m.getConnection(), TABLENAME, false); 155 Thread.sleep(100); 156 } 157 LOG.info("Regions: " + Joiner.on(',').join(tableRegions)); 158 // We have three regions because one is split-in-progress 159 assertEquals(3, tableRegions.size()); 160 LOG.info("Making sure we can call getTableRegionClosest while opening"); 161 Pair<RegionInfo, ServerName> pair = getTableRegionForRow(m, TABLENAME, Bytes.toBytes("cde")); 162 LOG.info("Result is: " + pair); 163 Pair<RegionInfo, ServerName> tableRegionFromName = 164 MetaTableAccessor.getRegion(m.getConnection(), pair.getFirst().getRegionName()); 165 assertTrue(RegionInfo.COMPARATOR.compare(tableRegionFromName.getFirst(), pair.getFirst()) == 0); 166 } 167 168 @Test 169 public void testMoveRegionWhenNotInitialized() { 170 MiniHBaseCluster cluster = TEST_UTIL.getHBaseCluster(); 171 HMaster m = cluster.getMaster(); 172 try { 173 m.setInitialized(false); // fake it, set back later 174 RegionInfo meta = RegionInfoBuilder.FIRST_META_REGIONINFO; 175 m.move(meta.getEncodedNameAsBytes(), null); 176 fail("Region should not be moved since master is not initialized"); 177 } catch (IOException ioe) { 178 assertTrue(ioe instanceof PleaseHoldException); 179 } finally { 180 m.setInitialized(true); 181 } 182 } 183 184 @Test 185 public void testMoveThrowsUnknownRegionException() throws IOException { 186 final TableName tableName = TableName.valueOf(name.getMethodName()); 187 HTableDescriptor htd = new HTableDescriptor(tableName); 188 HColumnDescriptor hcd = new HColumnDescriptor("value"); 189 htd.addFamily(hcd); 190 191 admin.createTable(htd, null); 192 try { 193 RegionInfo hri = RegionInfoBuilder.newBuilder(tableName).setStartKey(Bytes.toBytes("A")) 194 .setEndKey(Bytes.toBytes("Z")).build(); 195 admin.move(hri.getEncodedNameAsBytes()); 196 fail("Region should not be moved since it is fake"); 197 } catch (IOException ioe) { 198 assertTrue(ioe instanceof UnknownRegionException); 199 } finally { 200 TEST_UTIL.deleteTable(tableName); 201 } 202 } 203 204 @Test 205 public void testMoveThrowsPleaseHoldException() throws IOException { 206 final TableName tableName = TableName.valueOf(name.getMethodName()); 207 HMaster master = TEST_UTIL.getMiniHBaseCluster().getMaster(); 208 HTableDescriptor htd = new HTableDescriptor(tableName); 209 HColumnDescriptor hcd = new HColumnDescriptor("value"); 210 htd.addFamily(hcd); 211 212 admin.createTable(htd, null); 213 try { 214 List<RegionInfo> tableRegions = admin.getRegions(tableName); 215 216 master.setInitialized(false); // fake it, set back later 217 admin.move(tableRegions.get(0).getEncodedNameAsBytes()); 218 fail("Region should not be moved since master is not initialized"); 219 } catch (IOException ioe) { 220 assertTrue(StringUtils.stringifyException(ioe).contains("PleaseHoldException")); 221 } finally { 222 master.setInitialized(true); 223 TEST_UTIL.deleteTable(tableName); 224 } 225 } 226 227 @Test 228 public void testBlockingHbkc1WithLockFile() throws IOException { 229 // This is how the patch to the lock file is created inside in HBaseFsck. Too hard to use its 230 // actual method without disturbing HBaseFsck... Do the below mimic instead. 231 Path hbckLockPath = 232 new Path(HBaseFsck.getTmpDir(TEST_UTIL.getConfiguration()), HBaseFsck.HBCK_LOCK_FILE); 233 FileSystem fs = TEST_UTIL.getTestFileSystem(); 234 assertTrue(fs.exists(hbckLockPath)); 235 TEST_UTIL.getMiniHBaseCluster() 236 .killMaster(TEST_UTIL.getMiniHBaseCluster().getMaster().getServerName()); 237 assertTrue(fs.exists(hbckLockPath)); 238 TEST_UTIL.getMiniHBaseCluster().startMaster(); 239 TEST_UTIL.waitFor(30000, () -> TEST_UTIL.getMiniHBaseCluster().getMaster() != null 240 && TEST_UTIL.getMiniHBaseCluster().getMaster().isInitialized()); 241 assertTrue(fs.exists(hbckLockPath)); 242 // Start a second Master. Should be fine. 243 TEST_UTIL.getMiniHBaseCluster().startMaster(); 244 assertTrue(fs.exists(hbckLockPath)); 245 fs.delete(hbckLockPath, true); 246 assertFalse(fs.exists(hbckLockPath)); 247 // Kill all Masters. 248 TEST_UTIL.getMiniHBaseCluster().getLiveMasterThreads().stream() 249 .map(sn -> sn.getMaster().getServerName()).forEach(sn -> { 250 try { 251 TEST_UTIL.getMiniHBaseCluster().killMaster(sn); 252 } catch (IOException e) { 253 e.printStackTrace(); 254 } 255 }); 256 // Start a new one. 257 TEST_UTIL.getMiniHBaseCluster().startMaster(); 258 TEST_UTIL.waitFor(30000, () -> TEST_UTIL.getMiniHBaseCluster().getMaster() != null 259 && TEST_UTIL.getMiniHBaseCluster().getMaster().isInitialized()); 260 // Assert lock gets put in place again. 261 assertTrue(fs.exists(hbckLockPath)); 262 } 263 264 @Test 265 public void testInstallShutdownHook() { 266 // Test for HBASE-26977 267 assertTrue(TEST_UTIL.getMiniHBaseCluster().getMaster().isShutdownHookInstalled()); 268 } 269}