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