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.janitor; 019 020import static org.junit.Assert.assertFalse; 021import static org.junit.Assert.assertNotNull; 022import static org.junit.Assert.assertNull; 023import static org.junit.Assert.assertTrue; 024 025import java.io.IOException; 026import java.util.ArrayList; 027import java.util.List; 028import java.util.concurrent.ExecutionException; 029import org.apache.hadoop.hbase.CatalogFamilyFormat; 030import org.apache.hadoop.hbase.HBaseClassTestRule; 031import org.apache.hadoop.hbase.HBaseTestingUtil; 032import org.apache.hadoop.hbase.HRegionLocation; 033import org.apache.hadoop.hbase.MetaMockingUtil; 034import org.apache.hadoop.hbase.MetaTableAccessor; 035import org.apache.hadoop.hbase.TableName; 036import org.apache.hadoop.hbase.TableNameTestRule; 037import org.apache.hadoop.hbase.Waiter; 038import org.apache.hadoop.hbase.client.Admin; 039import org.apache.hadoop.hbase.client.Connection; 040import org.apache.hadoop.hbase.client.ConnectionFactory; 041import org.apache.hadoop.hbase.client.Get; 042import org.apache.hadoop.hbase.client.RegionInfo; 043import org.apache.hadoop.hbase.client.RegionLocator; 044import org.apache.hadoop.hbase.client.Result; 045import org.apache.hadoop.hbase.client.Table; 046import org.apache.hadoop.hbase.master.HMaster; 047import org.apache.hadoop.hbase.master.ServerManager; 048import org.apache.hadoop.hbase.master.assignment.AssignmentManager; 049import org.apache.hadoop.hbase.master.assignment.GCRegionProcedure; 050import org.apache.hadoop.hbase.master.procedure.MasterProcedureEnv; 051import org.apache.hadoop.hbase.procedure2.Procedure; 052import org.apache.hadoop.hbase.procedure2.ProcedureExecutor; 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.EnvironmentEdgeManager; 057import org.apache.hadoop.hbase.util.PairOfSameType; 058import org.apache.hadoop.hbase.util.Threads; 059import org.junit.AfterClass; 060import org.junit.BeforeClass; 061import org.junit.ClassRule; 062import org.junit.Rule; 063import org.junit.Test; 064import org.junit.experimental.categories.Category; 065import org.slf4j.Logger; 066import org.slf4j.LoggerFactory; 067 068@Category({ MasterTests.class, MediumTests.class }) 069public class TestCatalogJanitorInMemoryStates { 070 071 @ClassRule 072 public static final HBaseClassTestRule CLASS_RULE = 073 HBaseClassTestRule.forClass(TestCatalogJanitorInMemoryStates.class); 074 075 private static final Logger LOG = LoggerFactory.getLogger(TestCatalogJanitorInMemoryStates.class); 076 077 private static final HBaseTestingUtil TEST_UTIL = new HBaseTestingUtil(); 078 079 private static byte[] FAMILY = Bytes.toBytes("testFamily"); 080 081 @Rule 082 public final TableNameTestRule name = new TableNameTestRule(); 083 084 @BeforeClass 085 public static void setUpBeforeClass() throws Exception { 086 TEST_UTIL.startMiniCluster(1); 087 } 088 089 @AfterClass 090 public static void tearDownAfterClass() throws Exception { 091 TEST_UTIL.shutdownMiniCluster(); 092 } 093 094 /** 095 * Test clearing a split parent from memory. 096 */ 097 @Test 098 public void testInMemoryParentCleanup() 099 throws IOException, InterruptedException, ExecutionException { 100 HMaster master = TEST_UTIL.getHBaseCluster().getMaster(); 101 final AssignmentManager am = master.getAssignmentManager(); 102 final ServerManager sm = master.getServerManager(); 103 104 Admin admin = TEST_UTIL.getAdmin(); 105 admin.catalogJanitorSwitch(false); 106 107 final TableName tableName = name.getTableName(); 108 Table t = TEST_UTIL.createTable(tableName, FAMILY); 109 TEST_UTIL.loadTable(t, FAMILY, false); 110 111 RegionLocator locator = TEST_UTIL.getConnection().getRegionLocator(tableName); 112 List<HRegionLocation> allRegionLocations = locator.getAllRegionLocations(); 113 114 // We need to create a valid split with daughter regions 115 HRegionLocation parent = allRegionLocations.get(0); 116 List<HRegionLocation> daughters = splitRegion(parent.getRegion()); 117 LOG.info("Parent region: " + parent); 118 LOG.info("Daughter regions: " + daughters); 119 assertNotNull("Should have found daughter regions for " + parent, daughters); 120 121 assertNotNull("Parent region should exist in RegionStates", 122 am.getRegionStates().getRegionStateNodeFromName(parent.getRegion().getRegionName())); 123 assertTrue("Parent region should exist in ServerManager", 124 sm.isRegionInServerManagerStates(parent.getRegion())); 125 126 // clean the parent 127 Result r = MetaMockingUtil.getMetaTableRowResult(parent.getRegion(), null, 128 daughters.get(0).getRegion(), daughters.get(1).getRegion()); 129 CatalogJanitor.cleanParent(master, parent.getRegion(), r); 130 131 // wait for procedures to complete 132 Waiter.waitFor(TEST_UTIL.getConfiguration(), 10 * 1000, new Waiter.Predicate<Exception>() { 133 @Override 134 public boolean evaluate() throws Exception { 135 ProcedureExecutor<MasterProcedureEnv> pe = master.getMasterProcedureExecutor(); 136 for (Procedure<MasterProcedureEnv> proc : pe.getProcedures()) { 137 if (proc.getClass().isAssignableFrom(GCRegionProcedure.class) && proc.isFinished()) { 138 return true; 139 } 140 } 141 return false; 142 } 143 }); 144 145 assertNull("Parent region should have been removed from RegionStates", 146 am.getRegionStates().getRegionStateNodeFromName(parent.getRegion().getRegionName())); 147 assertFalse("Parent region should have been removed from ServerManager", 148 sm.isRegionInServerManagerStates(parent.getRegion())); 149 150 } 151 152 /** 153 * Splits a region 154 * @param r Region to split. 155 * @return List of region locations 156 */ 157 private List<HRegionLocation> splitRegion(final RegionInfo r) 158 throws IOException, InterruptedException, ExecutionException { 159 List<HRegionLocation> locations = new ArrayList<>(); 160 // Split this table in two. 161 Admin admin = TEST_UTIL.getAdmin(); 162 Connection connection = TEST_UTIL.getConnection(); 163 admin.splitRegionAsync(r.getEncodedNameAsBytes()).get(); 164 admin.close(); 165 PairOfSameType<RegionInfo> regions = waitOnDaughters(r); 166 if (regions != null) { 167 try (RegionLocator rl = connection.getRegionLocator(r.getTable())) { 168 locations.add(rl.getRegionLocation(regions.getFirst().getEncodedNameAsBytes())); 169 locations.add(rl.getRegionLocation(regions.getSecond().getEncodedNameAsBytes())); 170 } 171 return locations; 172 } 173 return locations; 174 } 175 176 /* 177 * Wait on region split. May return because we waited long enough on the split and it didn't 178 * happen. Caller should check. 179 * @return Daughter regions; caller needs to check table actually split. 180 */ 181 private PairOfSameType<RegionInfo> waitOnDaughters(final RegionInfo r) throws IOException { 182 long start = EnvironmentEdgeManager.currentTime(); 183 PairOfSameType<RegionInfo> pair = null; 184 try (Connection conn = ConnectionFactory.createConnection(TEST_UTIL.getConfiguration()); 185 Table metaTable = conn.getTable(TableName.META_TABLE_NAME)) { 186 Result result = null; 187 RegionInfo region = null; 188 while ((EnvironmentEdgeManager.currentTime() - start) < 60000) { 189 result = metaTable.get(new Get(r.getRegionName())); 190 if (result == null) { 191 break; 192 } 193 region = CatalogFamilyFormat.getRegionInfo(result); 194 if (region.isSplitParent()) { 195 LOG.debug(region.toString() + " IS a parent!"); 196 pair = MetaTableAccessor.getDaughterRegions(result); 197 break; 198 } 199 Threads.sleep(100); 200 } 201 202 if (pair.getFirst() == null || pair.getSecond() == null) { 203 throw new IOException("Failed to get daughters, for parent region: " + r); 204 } 205 return pair; 206 } 207 } 208}