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.assignment; 019 020import static org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProcedureProtos.RegionStateTransitionState.REGION_STATE_TRANSITION_OPEN; 021import static org.junit.Assert.assertTrue; 022 023import java.io.IOException; 024import java.util.ArrayList; 025import java.util.Arrays; 026import java.util.List; 027import java.util.concurrent.CountDownLatch; 028import org.apache.hadoop.hbase.HBaseClassTestRule; 029import org.apache.hadoop.hbase.HBaseTestingUtility; 030import org.apache.hadoop.hbase.ServerName; 031import org.apache.hadoop.hbase.TableName; 032import org.apache.hadoop.hbase.client.Admin; 033import org.apache.hadoop.hbase.client.RegionInfo; 034import org.apache.hadoop.hbase.master.assignment.TransitRegionStateProcedure.TransitionType; 035import org.apache.hadoop.hbase.master.procedure.MasterProcedureEnv; 036import org.apache.hadoop.hbase.procedure2.Procedure; 037import org.apache.hadoop.hbase.procedure2.ProcedureSuspendedException; 038import org.apache.hadoop.hbase.procedure2.ProcedureYieldException; 039import org.apache.hadoop.hbase.testclassification.LargeTests; 040import org.apache.hadoop.hbase.util.Bytes; 041import org.junit.AfterClass; 042import org.junit.Before; 043import org.junit.BeforeClass; 044import org.junit.ClassRule; 045import org.junit.Rule; 046import org.junit.Test; 047import org.junit.experimental.categories.Category; 048import org.junit.rules.TestName; 049import org.slf4j.Logger; 050import org.slf4j.LoggerFactory; 051 052import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProcedureProtos.RegionStateTransitionState; 053 054/** 055 * Tests bypass on a region assign/unassign 056 */ 057@Category({ LargeTests.class }) 058public class TestRegionBypass { 059 private final static Logger LOG = LoggerFactory.getLogger(TestRegionBypass.class); 060 061 @ClassRule 062 public static final HBaseClassTestRule CLASS_RULE = 063 HBaseClassTestRule.forClass(TestRegionBypass.class); 064 065 @Rule 066 public TestName name = new TestName(); 067 068 private static final HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility(); 069 private TableName tableName; 070 071 @BeforeClass 072 public static void startCluster() throws Exception { 073 TEST_UTIL.startMiniCluster(2); 074 } 075 076 @AfterClass 077 public static void stopCluster() throws Exception { 078 TEST_UTIL.shutdownMiniCluster(); 079 } 080 081 @Before 082 public void before() throws IOException { 083 this.tableName = TableName.valueOf(this.name.getMethodName()); 084 // Create a table. Has one region at least. 085 TEST_UTIL.createTable(this.tableName, Bytes.toBytes("cf")); 086 087 } 088 089 @Test 090 public void testBypass() throws IOException, InterruptedException { 091 Admin admin = TEST_UTIL.getAdmin(); 092 MasterProcedureEnv env = 093 TEST_UTIL.getHBaseCluster().getMaster().getMasterProcedureExecutor().getEnvironment(); 094 List<RegionInfo> regions = admin.getRegions(this.tableName); 095 for (RegionInfo ri : regions) { 096 admin.unassign(ri.getRegionName(), false); 097 } 098 List<Long> pids = new ArrayList<>(regions.size()); 099 for (RegionInfo ri : regions) { 100 Procedure<MasterProcedureEnv> p = 101 new StallingAssignProcedure(env, ri, null, false, TransitionType.ASSIGN); 102 pids.add( 103 TEST_UTIL.getHBaseCluster().getMaster().getMasterProcedureExecutor().submitProcedure(p)); 104 } 105 for (Long pid : pids) { 106 while (!TEST_UTIL.getHBaseCluster().getMaster().getMasterProcedureExecutor().isStarted(pid)) { 107 Thread.sleep(100); 108 } 109 } 110 List<Procedure<MasterProcedureEnv>> ps = 111 TEST_UTIL.getHBaseCluster().getMaster().getMasterProcedureExecutor().getProcedures(); 112 for (Procedure<MasterProcedureEnv> p : ps) { 113 if (p instanceof StallingAssignProcedure) { 114 List<Boolean> bs = 115 TEST_UTIL.getHbck().bypassProcedure(Arrays.asList(p.getProcId()), 1000, true, false); 116 for (Boolean b : bs) { 117 LOG.info("BYPASSED {} {}", p.getProcId(), b); 118 } 119 } 120 } 121 // Try and assign WITHOUT override flag. Should fail!. 122 for (RegionInfo ri : regions) { 123 try { 124 admin.assign(ri.getRegionName()); 125 } catch (Throwable dnrioe) { 126 // Expected 127 LOG.info("Expected {}", dnrioe); 128 } 129 } 130 while ( 131 !TEST_UTIL.getHBaseCluster().getMaster().getMasterProcedureExecutor().getActiveProcIds() 132 .isEmpty() 133 ) { 134 Thread.sleep(100); 135 } 136 // Now assign with the override flag. 137 for (RegionInfo ri : regions) { 138 TEST_UTIL.getHbck().assigns(Arrays.<String> asList(ri.getEncodedName()), true); 139 } 140 while ( 141 !TEST_UTIL.getHBaseCluster().getMaster().getMasterProcedureExecutor().getActiveProcIds() 142 .isEmpty() 143 ) { 144 Thread.sleep(100); 145 } 146 for (RegionInfo ri : regions) { 147 assertTrue(ri.toString(), TEST_UTIL.getMiniHBaseCluster().getMaster().getAssignmentManager() 148 .getRegionStates().isRegionOnline(ri)); 149 } 150 } 151 152 /** 153 * An AssignProcedure that Stalls just before the finish. 154 */ 155 public static class StallingAssignProcedure extends TransitRegionStateProcedure { 156 public final CountDownLatch latch = new CountDownLatch(2); 157 158 public StallingAssignProcedure() { 159 } 160 161 public StallingAssignProcedure(MasterProcedureEnv env, RegionInfo hri, 162 ServerName assignCandidate, boolean forceNewPlan, TransitionType type) { 163 super(env, hri, assignCandidate, forceNewPlan, type); 164 init(env); 165 } 166 167 private void init(MasterProcedureEnv env) { 168 RegionStateNode regionNode = 169 env.getAssignmentManager().getRegionStates().getOrCreateRegionStateNode(getRegion()); 170 regionNode.setProcedure(this); 171 } 172 173 @Override 174 protected Flow executeFromState(MasterProcedureEnv env, RegionStateTransitionState state) 175 throws ProcedureSuspendedException, ProcedureYieldException, InterruptedException { 176 switch (state) { 177 case REGION_STATE_TRANSITION_GET_ASSIGN_CANDIDATE: 178 LOG.info("LATCH1 {}", this.latch.getCount()); 179 this.latch.countDown(); 180 setNextState(REGION_STATE_TRANSITION_OPEN); 181 return Flow.HAS_MORE_STATE; 182 case REGION_STATE_TRANSITION_OPEN: 183 if (latch.getCount() == 0) { 184 LOG.info("LATCH3 {}", this.latch.getCount()); 185 return Flow.NO_MORE_STATE; 186 } else { 187 LOG.info("LATCH2 {}", this.latch.getCount()); 188 return Flow.HAS_MORE_STATE; 189 } 190 default: 191 throw new UnsupportedOperationException("unhandled state=" + state); 192 } 193 } 194 } 195}