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