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; 028 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.Table; 046import org.apache.hadoop.hbase.client.TableState; 047import org.apache.hadoop.hbase.testclassification.MasterTests; 048import org.apache.hadoop.hbase.testclassification.MediumTests; 049import org.apache.hadoop.hbase.util.Bytes; 050import org.apache.hadoop.hbase.util.HBaseFsck; 051import org.apache.hadoop.hbase.util.Pair; 052import org.apache.hadoop.util.StringUtils; 053import org.junit.AfterClass; 054import org.junit.BeforeClass; 055import org.junit.ClassRule; 056import org.junit.Rule; 057import org.junit.Test; 058import org.junit.experimental.categories.Category; 059import org.junit.rules.TestName; 060import org.slf4j.Logger; 061import org.slf4j.LoggerFactory; 062 063import org.apache.hbase.thirdparty.com.google.common.base.Joiner; 064 065@Category({MasterTests.class, MediumTests.class}) 066public class TestMaster { 067 068 @ClassRule 069 public static final HBaseClassTestRule CLASS_RULE = 070 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 = 075 TableName.valueOf("TestMaster"); 076 private static final byte[] FAMILYNAME = Bytes.toBytes("fam"); 077 private static Admin admin; 078 079 @Rule 080 public TestName name = new TestName(); 081 082 @BeforeClass 083 public static void beforeAllTests() throws Exception { 084 // we will retry operations when PleaseHoldException is thrown 085 TEST_UTIL.getConfiguration().setInt(HConstants.HBASE_CLIENT_RETRIES_NUMBER, 3); 086 // Start a cluster of two regionservers. 087 TEST_UTIL.startMiniCluster(2); 088 admin = TEST_UTIL.getAdmin(); 089 } 090 091 @AfterClass 092 public static void afterAllTests() throws Exception { 093 TEST_UTIL.shutdownMiniCluster(); 094 } 095 096 @Test 097 @SuppressWarnings("deprecation") 098 public void testMasterOpsWhileSplitting() throws Exception { 099 MiniHBaseCluster cluster = TEST_UTIL.getHBaseCluster(); 100 HMaster m = cluster.getMaster(); 101 102 try (Table ht = TEST_UTIL.createTable(TABLENAME, FAMILYNAME)) { 103 assertTrue(m.getTableStateManager().isTableState(TABLENAME, TableState.State.ENABLED)); 104 TEST_UTIL.loadTable(ht, FAMILYNAME, false); 105 } 106 107 List<Pair<RegionInfo, ServerName>> tableRegions = MetaTableAccessor.getTableRegionsAndLocations( 108 m.getConnection(), TABLENAME); 109 LOG.info("Regions after load: " + Joiner.on(',').join(tableRegions)); 110 assertEquals(1, tableRegions.size()); 111 assertArrayEquals(HConstants.EMPTY_START_ROW, 112 tableRegions.get(0).getFirst().getStartKey()); 113 assertArrayEquals(HConstants.EMPTY_END_ROW, 114 tableRegions.get(0).getFirst().getEndKey()); 115 116 // Now trigger a split and stop when the split is in progress 117 LOG.info("Splitting table"); 118 TEST_UTIL.getAdmin().split(TABLENAME); 119 120 LOG.info("Making sure we can call getTableRegions while opening"); 121 while (tableRegions.size() < 3) { 122 tableRegions = MetaTableAccessor.getTableRegionsAndLocations(m.getConnection(), 123 TABLENAME, false); 124 Thread.sleep(100); 125 } 126 LOG.info("Regions: " + Joiner.on(',').join(tableRegions)); 127 // We have three regions because one is split-in-progress 128 assertEquals(3, tableRegions.size()); 129 LOG.info("Making sure we can call getTableRegionClosest while opening"); 130 Pair<RegionInfo, ServerName> pair = 131 m.getTableRegionForRow(TABLENAME, Bytes.toBytes("cde")); 132 LOG.info("Result is: " + pair); 133 Pair<RegionInfo, ServerName> tableRegionFromName = 134 MetaTableAccessor.getRegion(m.getConnection(), 135 pair.getFirst().getRegionName()); 136 assertTrue(RegionInfo.COMPARATOR.compare(tableRegionFromName.getFirst(), pair.getFirst()) == 0); 137 } 138 139 @Test 140 public void testMoveRegionWhenNotInitialized() { 141 MiniHBaseCluster cluster = TEST_UTIL.getHBaseCluster(); 142 HMaster m = cluster.getMaster(); 143 try { 144 m.setInitialized(false); // fake it, set back later 145 RegionInfo meta = RegionInfoBuilder.FIRST_META_REGIONINFO; 146 m.move(meta.getEncodedNameAsBytes(), null); 147 fail("Region should not be moved since master is not initialized"); 148 } catch (IOException ioe) { 149 assertTrue(ioe instanceof PleaseHoldException); 150 } finally { 151 m.setInitialized(true); 152 } 153 } 154 155 @Test 156 public void testMoveThrowsUnknownRegionException() throws IOException { 157 final TableName tableName = TableName.valueOf(name.getMethodName()); 158 HTableDescriptor htd = new HTableDescriptor(tableName); 159 HColumnDescriptor hcd = new HColumnDescriptor("value"); 160 htd.addFamily(hcd); 161 162 admin.createTable(htd, null); 163 try { 164 RegionInfo hri = RegionInfoBuilder.newBuilder(tableName) 165 .setStartKey(Bytes.toBytes("A")) 166 .setEndKey(Bytes.toBytes("Z")) 167 .build(); 168 admin.move(hri.getEncodedNameAsBytes(), null); 169 fail("Region should not be moved since it is fake"); 170 } catch (IOException ioe) { 171 assertTrue(ioe instanceof UnknownRegionException); 172 } finally { 173 TEST_UTIL.deleteTable(tableName); 174 } 175 } 176 177 @Test 178 public void testMoveThrowsPleaseHoldException() throws IOException { 179 final TableName tableName = TableName.valueOf(name.getMethodName()); 180 HMaster master = TEST_UTIL.getMiniHBaseCluster().getMaster(); 181 HTableDescriptor htd = new HTableDescriptor(tableName); 182 HColumnDescriptor hcd = new HColumnDescriptor("value"); 183 htd.addFamily(hcd); 184 185 admin.createTable(htd, null); 186 try { 187 List<RegionInfo> tableRegions = admin.getRegions(tableName); 188 189 master.setInitialized(false); // fake it, set back later 190 admin.move(tableRegions.get(0).getEncodedNameAsBytes(), null); 191 fail("Region should not be moved since master is not initialized"); 192 } catch (IOException ioe) { 193 assertTrue(StringUtils.stringifyException(ioe).contains("PleaseHoldException")); 194 } finally { 195 master.setInitialized(true); 196 TEST_UTIL.deleteTable(tableName); 197 } 198 } 199 200 @Test 201 public void testBlockingHbkc1WithLockFile() throws IOException { 202 // This is how the patch to the lock file is created inside in HBaseFsck. Too hard to use its 203 // actual method without disturbing HBaseFsck... Do the below mimic instead. 204 Path hbckLockPath = new Path(HBaseFsck.getTmpDir(TEST_UTIL.getConfiguration()), 205 HBaseFsck.HBCK_LOCK_FILE); 206 FileSystem fs = TEST_UTIL.getTestFileSystem(); 207 assertTrue(fs.exists(hbckLockPath)); 208 TEST_UTIL.getMiniHBaseCluster(). 209 killMaster(TEST_UTIL.getMiniHBaseCluster().getMaster().getServerName()); 210 assertTrue(fs.exists(hbckLockPath)); 211 TEST_UTIL.getMiniHBaseCluster().startMaster(); 212 TEST_UTIL.waitFor(30000, () -> TEST_UTIL.getMiniHBaseCluster().getMaster() != null && 213 TEST_UTIL.getMiniHBaseCluster().getMaster().isInitialized()); 214 assertTrue(fs.exists(hbckLockPath)); 215 // Start a second Master. Should be fine. 216 TEST_UTIL.getMiniHBaseCluster().startMaster(); 217 assertTrue(fs.exists(hbckLockPath)); 218 fs.delete(hbckLockPath, true); 219 assertFalse(fs.exists(hbckLockPath)); 220 // Kill all Masters. 221 TEST_UTIL.getMiniHBaseCluster().getLiveMasterThreads().stream(). 222 map(sn -> sn.getMaster().getServerName()).forEach(sn -> { 223 try { 224 TEST_UTIL.getMiniHBaseCluster().killMaster(sn); 225 } catch (IOException e) { 226 e.printStackTrace(); 227 } 228 }); 229 // Start a new one. 230 TEST_UTIL.getMiniHBaseCluster().startMaster(); 231 TEST_UTIL.waitFor(30000, () -> TEST_UTIL.getMiniHBaseCluster().getMaster() != null && 232 TEST_UTIL.getMiniHBaseCluster().getMaster().isInitialized()); 233 // Assert lock gets put in place again. 234 assertTrue(fs.exists(hbckLockPath)); 235 } 236} 237