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.client; 019 020import static org.junit.Assert.assertEquals; 021import static org.junit.Assert.assertNotEquals; 022import static org.junit.Assert.assertTrue; 023import java.io.IOException; 024import java.util.Arrays; 025import java.util.HashMap; 026import java.util.List; 027import java.util.Map; 028import java.util.Optional; 029import java.util.concurrent.CountDownLatch; 030import java.util.stream.Collectors; 031import org.apache.hadoop.hbase.Coprocessor; 032import org.apache.hadoop.hbase.CoprocessorEnvironment; 033import org.apache.hadoop.hbase.HBaseClassTestRule; 034import org.apache.hadoop.hbase.HBaseTestingUtility; 035import org.apache.hadoop.hbase.ServerName; 036import org.apache.hadoop.hbase.TableName; 037import org.apache.hadoop.hbase.coprocessor.MasterCoprocessor; 038import org.apache.hadoop.hbase.coprocessor.MasterCoprocessorEnvironment; 039import org.apache.hadoop.hbase.coprocessor.MasterObserver; 040import org.apache.hadoop.hbase.coprocessor.ObserverContext; 041import org.apache.hadoop.hbase.master.HMaster; 042import org.apache.hadoop.hbase.master.RegionState; 043import org.apache.hadoop.hbase.master.assignment.AssignmentManager; 044import org.apache.hadoop.hbase.master.procedure.MasterProcedureEnv; 045import org.apache.hadoop.hbase.master.procedure.TableProcedureInterface; 046import org.apache.hadoop.hbase.procedure2.Procedure; 047import org.apache.hadoop.hbase.procedure2.ProcedureExecutor; 048import org.apache.hadoop.hbase.procedure2.ProcedureSuspendedException; 049import org.apache.hadoop.hbase.procedure2.ProcedureTestingUtility; 050import org.apache.hadoop.hbase.regionserver.HRegionServer; 051import org.apache.hadoop.hbase.testclassification.ClientTests; 052import org.apache.hadoop.hbase.testclassification.LargeTests; 053import org.apache.hadoop.hbase.util.Bytes; 054import org.apache.hadoop.hbase.util.Pair; 055import org.junit.AfterClass; 056import org.junit.Before; 057import org.junit.BeforeClass; 058import org.junit.ClassRule; 059import org.junit.Rule; 060import org.junit.Test; 061import org.junit.experimental.categories.Category; 062import org.junit.rules.TestName; 063import org.junit.runner.RunWith; 064import org.junit.runners.Parameterized; 065import org.junit.runners.Parameterized.Parameter; 066import org.junit.runners.Parameterized.Parameters; 067import org.slf4j.Logger; 068import org.slf4j.LoggerFactory; 069import org.apache.hbase.thirdparty.com.google.common.io.Closeables; 070import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil; 071 072/** 073 * Class to test HBaseHbck. Spins up the minicluster once at test start and then takes it down 074 * afterward. Add any testing of HBaseHbck functionality here. 075 */ 076@RunWith(Parameterized.class) 077@Category({ LargeTests.class, ClientTests.class }) 078public class TestHbck { 079 @ClassRule 080 public static final HBaseClassTestRule CLASS_RULE = HBaseClassTestRule.forClass(TestHbck.class); 081 082 private static final Logger LOG = LoggerFactory.getLogger(TestHbck.class); 083 private final static HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility(); 084 085 @Rule 086 public TestName name = new TestName(); 087 088 @SuppressWarnings("checkstyle:VisibilityModifier") @Parameter 089 public boolean async; 090 091 private static final TableName TABLE_NAME = TableName.valueOf(TestHbck.class.getSimpleName()); 092 093 private static ProcedureExecutor<MasterProcedureEnv> procExec; 094 095 private static AsyncConnection ASYNC_CONN; 096 097 @Parameters(name = "{index}: async={0}") 098 public static List<Object[]> params() { 099 return Arrays.asList(new Object[] { false }, new Object[] { true }); 100 } 101 102 private Hbck getHbck() throws Exception { 103 if (async) { 104 return ASYNC_CONN.getHbck().get(); 105 } else { 106 return TEST_UTIL.getHbck(); 107 } 108 } 109 110 @BeforeClass 111 public static void setUpBeforeClass() throws Exception { 112 TEST_UTIL.startMiniCluster(3); 113 TEST_UTIL.createMultiRegionTable(TABLE_NAME, Bytes.toBytes("family1"), 5); 114 procExec = TEST_UTIL.getMiniHBaseCluster().getMaster().getMasterProcedureExecutor(); 115 ASYNC_CONN = ConnectionFactory.createAsyncConnection(TEST_UTIL.getConfiguration()).get(); 116 TEST_UTIL.getHBaseCluster().getMaster().getMasterCoprocessorHost().load( 117 FailingMergeAfterMetaUpdatedMasterObserver.class, Coprocessor.PRIORITY_USER, 118 TEST_UTIL.getHBaseCluster().getMaster().getConfiguration()); 119 TEST_UTIL.getHBaseCluster().getMaster().getMasterCoprocessorHost().load( 120 FailingSplitAfterMetaUpdatedMasterObserver.class, Coprocessor.PRIORITY_USER, 121 TEST_UTIL.getHBaseCluster().getMaster().getConfiguration()); 122 } 123 124 @AfterClass 125 public static void tearDownAfterClass() throws Exception { 126 Closeables.close(ASYNC_CONN, true); 127 TEST_UTIL.shutdownMiniCluster(); 128 } 129 130 @Before 131 public void setUp() throws IOException { 132 TEST_UTIL.ensureSomeRegionServersAvailable(3); 133 } 134 135 public static class SuspendProcedure extends 136 ProcedureTestingUtility.NoopProcedure<MasterProcedureEnv> implements TableProcedureInterface { 137 public SuspendProcedure() { 138 super(); 139 } 140 141 @SuppressWarnings({ "rawtypes", "unchecked" }) 142 @Override 143 protected Procedure[] execute(final MasterProcedureEnv env) throws ProcedureSuspendedException { 144 // Always suspend the procedure 145 throw new ProcedureSuspendedException(); 146 } 147 148 @Override 149 public TableName getTableName() { 150 return TABLE_NAME; 151 } 152 153 @Override 154 public TableOperationType getTableOperationType() { 155 return TableOperationType.READ; 156 } 157 } 158 159 @Test 160 public void testBypassProcedure() throws Exception { 161 // SuspendProcedure 162 final SuspendProcedure proc = new SuspendProcedure(); 163 long procId = procExec.submitProcedure(proc); 164 Thread.sleep(500); 165 166 // bypass the procedure 167 List<Long> pids = Arrays.<Long> asList(procId); 168 List<Boolean> results = getHbck().bypassProcedure(pids, 30000, false, false); 169 assertTrue("Failed to by pass procedure!", results.get(0)); 170 TEST_UTIL.waitFor(5000, () -> proc.isSuccess() && proc.isBypass()); 171 LOG.info("{} finished", proc); 172 } 173 174 @Test 175 public void testSetTableStateInMeta() throws Exception { 176 Hbck hbck = getHbck(); 177 // set table state to DISABLED 178 hbck.setTableStateInMeta(new TableState(TABLE_NAME, TableState.State.DISABLED)); 179 // Method {@link Hbck#setTableStateInMeta()} returns previous state, which in this case 180 // will be DISABLED 181 TableState prevState = 182 hbck.setTableStateInMeta(new TableState(TABLE_NAME, TableState.State.ENABLED)); 183 assertTrue("Incorrect previous state! expeced=DISABLED, found=" + prevState.getState(), 184 prevState.isDisabled()); 185 } 186 187 @Test 188 public void testSetRegionStateInMeta() throws Exception { 189 Hbck hbck = getHbck(); 190 Admin admin = TEST_UTIL.getAdmin(); 191 final List<RegionInfo> regions = admin.getRegions(TABLE_NAME); 192 final AssignmentManager am = TEST_UTIL.getHBaseCluster().getMaster().getAssignmentManager(); 193 Map<String, RegionState.State> prevStates = new HashMap<>(); 194 Map<String, RegionState.State> newStates = new HashMap<>(); 195 final Map<String, Pair<RegionState.State, RegionState.State>> regionsMap = new HashMap<>(); 196 regions.forEach(r -> { 197 RegionState prevState = am.getRegionStates().getRegionState(r); 198 prevStates.put(r.getEncodedName(), prevState.getState()); 199 newStates.put(r.getEncodedName(), RegionState.State.CLOSED); 200 regionsMap.put(r.getEncodedName(), 201 new Pair<>(prevState.getState(), RegionState.State.CLOSED)); 202 }); 203 final Map<String, RegionState.State> result = hbck.setRegionStateInMeta(newStates); 204 result.forEach((k, v) -> { 205 RegionState.State prevState = regionsMap.get(k).getFirst(); 206 assertEquals(prevState, v); 207 }); 208 regions.forEach(r -> { 209 RegionState cachedState = am.getRegionStates().getRegionState(r.getEncodedName()); 210 RegionState.State newState = regionsMap.get(r.getEncodedName()).getSecond(); 211 assertEquals(newState, cachedState.getState()); 212 }); 213 hbck.setRegionStateInMeta(prevStates); 214 } 215 216 @Test 217 public void testAssigns() throws Exception { 218 Hbck hbck = getHbck(); 219 try (Admin admin = TEST_UTIL.getConnection().getAdmin()) { 220 List<RegionInfo> regions = admin.getRegions(TABLE_NAME); 221 for (RegionInfo ri : regions) { 222 RegionState rs = TEST_UTIL.getHBaseCluster().getMaster().getAssignmentManager() 223 .getRegionStates().getRegionState(ri.getEncodedName()); 224 LOG.info("RS: {}", rs.toString()); 225 } 226 List<Long> pids = 227 hbck.unassigns(regions.stream().map(r -> r.getEncodedName()).collect(Collectors.toList())); 228 waitOnPids(pids); 229 // Rerun the unassign. Should fail for all Regions since they already unassigned; failed 230 // unassign will manifest as all pids being -1 (ever since HBASE-24885). 231 pids = 232 hbck.unassigns(regions.stream().map(r -> r.getEncodedName()).collect(Collectors.toList())); 233 waitOnPids(pids); 234 for (long pid: pids) { 235 assertEquals(Procedure.NO_PROC_ID, pid); 236 } 237 // If we pass override, then we should be able to unassign EVEN THOUGH Regions already 238 // unassigned.... makes for a mess but operator might want to do this at an extreme when 239 // doing fixup of broke cluster. 240 pids = 241 hbck.unassigns(regions.stream().map(r -> r.getEncodedName()).collect(Collectors.toList()), 242 true); 243 waitOnPids(pids); 244 for (long pid: pids) { 245 assertNotEquals(Procedure.NO_PROC_ID, pid); 246 } 247 // Clean-up by bypassing all the unassigns we just made so tests can continue. 248 hbck.bypassProcedure(pids, 10000, true, true); 249 for (RegionInfo ri : regions) { 250 RegionState rs = TEST_UTIL.getHBaseCluster().getMaster().getAssignmentManager() 251 .getRegionStates().getRegionState(ri.getEncodedName()); 252 LOG.info("RS: {}", rs.toString()); 253 assertTrue(rs.toString(), rs.isClosed()); 254 } 255 pids = 256 hbck.assigns(regions.stream().map(r -> r.getEncodedName()).collect(Collectors.toList())); 257 waitOnPids(pids); 258 // Rerun the assign. Should fail for all Regions since they already assigned; failed 259 // assign will manifest as all pids being -1 (ever since HBASE-24885). 260 pids = 261 hbck.assigns(regions.stream().map(r -> r.getEncodedName()).collect(Collectors.toList())); 262 for (long pid: pids) { 263 assertEquals(Procedure.NO_PROC_ID, pid); 264 } 265 for (RegionInfo ri : regions) { 266 RegionState rs = TEST_UTIL.getHBaseCluster().getMaster().getAssignmentManager() 267 .getRegionStates().getRegionState(ri.getEncodedName()); 268 LOG.info("RS: {}", rs.toString()); 269 assertTrue(rs.toString(), rs.isOpened()); 270 } 271 // What happens if crappy region list passed? 272 pids = hbck.assigns( 273 Arrays.stream(new String[] { "a", "some rubbish name" }).collect(Collectors.toList())); 274 for (long pid : pids) { 275 assertEquals(Procedure.NO_PROC_ID, pid); 276 } 277 } 278 } 279 280 @Test 281 public void testScheduleSCP() throws Exception { 282 HRegionServer testRs = TEST_UTIL.getRSForFirstRegionInTable(TABLE_NAME); 283 TEST_UTIL.loadTable(TEST_UTIL.getConnection().getTable(TABLE_NAME), Bytes.toBytes("family1"), 284 true); 285 ServerName serverName = testRs.getServerName(); 286 Hbck hbck = getHbck(); 287 List<Long> pids = 288 hbck.scheduleServerCrashProcedure(Arrays.asList(ProtobufUtil.toServerName(serverName))); 289 assertTrue(pids.get(0) > 0); 290 LOG.info("pid is {}", pids.get(0)); 291 292 List<Long> newPids = 293 hbck.scheduleServerCrashProcedure(Arrays.asList(ProtobufUtil.toServerName(serverName))); 294 assertTrue(newPids.get(0) < 0); 295 LOG.info("pid is {}", newPids.get(0)); 296 waitOnPids(pids); 297 } 298 299 @Test 300 public void testRunHbckChore() throws Exception { 301 HMaster master = TEST_UTIL.getMiniHBaseCluster().getMaster(); 302 long endTimestamp = master.getHbckChore().getCheckingEndTimestamp(); 303 Hbck hbck = getHbck(); 304 boolean ran = false; 305 while (!ran) { 306 ran = hbck.runHbckChore(); 307 if (ran) { 308 assertTrue(master.getHbckChore().getCheckingEndTimestamp() > endTimestamp); 309 } 310 } 311 } 312 313 public static class FailingSplitAfterMetaUpdatedMasterObserver 314 implements MasterCoprocessor, MasterObserver { 315 @SuppressWarnings("checkstyle:VisibilityModifier") public volatile CountDownLatch latch; 316 317 @Override 318 public void start(CoprocessorEnvironment e) throws IOException { 319 resetLatch(); 320 } 321 322 @Override 323 public Optional<MasterObserver> getMasterObserver() { 324 return Optional.of(this); 325 } 326 327 @Override 328 public void preSplitRegionAfterMETAAction(ObserverContext<MasterCoprocessorEnvironment> ctx) 329 throws IOException { 330 LOG.info("I'm here"); 331 latch.countDown(); 332 throw new IOException("this procedure will fail at here forever"); 333 } 334 335 public void resetLatch() { 336 this.latch = new CountDownLatch(1); 337 } 338 } 339 340 public static class FailingMergeAfterMetaUpdatedMasterObserver 341 implements MasterCoprocessor, MasterObserver { 342 @SuppressWarnings("checkstyle:VisibilityModifier") public volatile CountDownLatch latch; 343 344 @Override 345 public void start(CoprocessorEnvironment e) throws IOException { 346 resetLatch(); 347 } 348 349 @Override 350 public Optional<MasterObserver> getMasterObserver() { 351 return Optional.of(this); 352 } 353 354 public void resetLatch() { 355 this.latch = new CountDownLatch(1); 356 } 357 358 @Override 359 public void postMergeRegionsCommitAction( 360 final ObserverContext<MasterCoprocessorEnvironment> ctx, final RegionInfo[] regionsToMerge, 361 final RegionInfo mergedRegion) throws IOException { 362 latch.countDown(); 363 throw new IOException("this procedure will fail at here forever"); 364 } 365 } 366 367 private void waitOnPids(List<Long> pids) { 368 TEST_UTIL.waitFor(60000, () -> pids.stream().allMatch(procExec::isFinished)); 369 } 370}