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