HBASE-24015: Test for Assign and Unassign of Regions on RegionServer on failure (#1898)
Signed-off-by: Duo Zhang <zhangduo@apache.org> Signed-off-by: Viraj Jasani <vjasani@apache.org>
This commit is contained in:
parent
2567d15218
commit
db4d539190
|
@ -123,7 +123,7 @@ public class TransitRegionStateProcedure
|
||||||
public TransitRegionStateProcedure() {
|
public TransitRegionStateProcedure() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setInitalAndLastState() {
|
private void setInitialAndLastState() {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case ASSIGN:
|
case ASSIGN:
|
||||||
initialState = RegionStateTransitionState.REGION_STATE_TRANSITION_GET_ASSIGN_CANDIDATE;
|
initialState = RegionStateTransitionState.REGION_STATE_TRANSITION_GET_ASSIGN_CANDIDATE;
|
||||||
|
@ -150,7 +150,7 @@ public class TransitRegionStateProcedure
|
||||||
this.assignCandidate = assignCandidate;
|
this.assignCandidate = assignCandidate;
|
||||||
this.forceNewPlan = forceNewPlan;
|
this.forceNewPlan = forceNewPlan;
|
||||||
this.type = type;
|
this.type = type;
|
||||||
setInitalAndLastState();
|
setInitialAndLastState();
|
||||||
|
|
||||||
// when do reopen TRSP, let the rs know the targetServer so it can keep some info on close
|
// when do reopen TRSP, let the rs know the targetServer so it can keep some info on close
|
||||||
if (type == TransitionType.REOPEN) {
|
if (type == TransitionType.REOPEN) {
|
||||||
|
@ -506,7 +506,7 @@ public class TransitRegionStateProcedure
|
||||||
RegionStateTransitionStateData data =
|
RegionStateTransitionStateData data =
|
||||||
serializer.deserialize(RegionStateTransitionStateData.class);
|
serializer.deserialize(RegionStateTransitionStateData.class);
|
||||||
type = convert(data.getType());
|
type = convert(data.getType());
|
||||||
setInitalAndLastState();
|
setInitialAndLastState();
|
||||||
forceNewPlan = data.getForceNewPlan();
|
forceNewPlan = data.getForceNewPlan();
|
||||||
if (data.hasAssignCandidate()) {
|
if (data.hasAssignCandidate()) {
|
||||||
assignCandidate = ProtobufUtil.toServerName(data.getAssignCandidate());
|
assignCandidate = ProtobufUtil.toServerName(data.getAssignCandidate());
|
||||||
|
|
|
@ -94,9 +94,8 @@ public class CloseRegionHandler extends EventHandler {
|
||||||
public void process() throws IOException {
|
public void process() throws IOException {
|
||||||
String name = regionInfo.getEncodedName();
|
String name = regionInfo.getEncodedName();
|
||||||
LOG.trace("Processing close of {}", name);
|
LOG.trace("Processing close of {}", name);
|
||||||
String encodedRegionName = regionInfo.getEncodedName();
|
|
||||||
// Check that this region is being served here
|
// Check that this region is being served here
|
||||||
HRegion region = (HRegion)rsServices.getRegion(encodedRegionName);
|
HRegion region = (HRegion)rsServices.getRegion(name);
|
||||||
try {
|
try {
|
||||||
if (region == null) {
|
if (region == null) {
|
||||||
LOG.warn("Received CLOSE for region {} but currently not serving - ignoring", name);
|
LOG.warn("Received CLOSE for region {} but currently not serving - ignoring", name);
|
||||||
|
|
|
@ -0,0 +1,130 @@
|
||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.hadoop.hbase.master.assignment;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
import org.apache.hadoop.hbase.HBaseClassTestRule;
|
||||||
|
import org.apache.hadoop.hbase.HBaseTestingUtility;
|
||||||
|
import org.apache.hadoop.hbase.TableName;
|
||||||
|
import org.apache.hadoop.hbase.client.RegionInfo;
|
||||||
|
import org.apache.hadoop.hbase.coprocessor.CoprocessorHost;
|
||||||
|
import org.apache.hadoop.hbase.coprocessor.ObserverContext;
|
||||||
|
import org.apache.hadoop.hbase.coprocessor.RegionCoprocessor;
|
||||||
|
import org.apache.hadoop.hbase.coprocessor.RegionCoprocessorEnvironment;
|
||||||
|
import org.apache.hadoop.hbase.coprocessor.RegionObserver;
|
||||||
|
import org.apache.hadoop.hbase.procedure2.ProcedureExecutor;
|
||||||
|
import org.apache.hadoop.hbase.procedure2.ProcedureTestingUtility;
|
||||||
|
import org.apache.hadoop.hbase.regionserver.HRegionServer;
|
||||||
|
import org.apache.hadoop.hbase.testclassification.MasterTests;
|
||||||
|
import org.apache.hadoop.hbase.testclassification.MediumTests;
|
||||||
|
import org.apache.hadoop.hbase.util.Bytes;
|
||||||
|
import org.apache.hadoop.hbase.util.JVMClusterUtil;
|
||||||
|
import org.junit.AfterClass;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.BeforeClass;
|
||||||
|
import org.junit.ClassRule;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.experimental.categories.Category;
|
||||||
|
|
||||||
|
@Category({ MasterTests.class, MediumTests.class })
|
||||||
|
public class TestExceptionInAssignRegion {
|
||||||
|
|
||||||
|
@ClassRule
|
||||||
|
public static final HBaseClassTestRule CLASS_RULE =
|
||||||
|
HBaseClassTestRule.forClass(TestExceptionInAssignRegion.class);
|
||||||
|
|
||||||
|
private static final HBaseTestingUtility UTIL = new HBaseTestingUtility();
|
||||||
|
|
||||||
|
private static final TableName TABLE_NAME = TableName.valueOf("test");
|
||||||
|
|
||||||
|
private static final CountDownLatch countDownLatch = new CountDownLatch(2);
|
||||||
|
|
||||||
|
private static final byte[] CF = Bytes.toBytes("cf");
|
||||||
|
|
||||||
|
@BeforeClass
|
||||||
|
public static void setUp() throws Exception {
|
||||||
|
UTIL.getConfiguration().setStrings(CoprocessorHost.REGION_COPROCESSOR_CONF_KEY,
|
||||||
|
ThrowInOpenCP.class.getName());
|
||||||
|
UTIL.startMiniCluster(3);
|
||||||
|
UTIL.getAdmin().balancerSwitch(false, true);
|
||||||
|
UTIL.createTable(TABLE_NAME, CF);
|
||||||
|
UTIL.waitTableAvailable(TABLE_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterClass
|
||||||
|
public static void tearDown() throws Exception {
|
||||||
|
UTIL.shutdownMiniCluster();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testExceptionInAssignRegion() {
|
||||||
|
ProcedureExecutor procedureExecutor =
|
||||||
|
UTIL.getMiniHBaseCluster().getMaster().getMasterProcedureExecutor();
|
||||||
|
|
||||||
|
JVMClusterUtil.RegionServerThread rsThread = null;
|
||||||
|
for (JVMClusterUtil.RegionServerThread t : UTIL.getMiniHBaseCluster()
|
||||||
|
.getRegionServerThreads()) {
|
||||||
|
if (!t.getRegionServer().getRegions(TABLE_NAME).isEmpty()) {
|
||||||
|
rsThread = t;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// find the rs and hri of the table
|
||||||
|
HRegionServer rs = rsThread.getRegionServer();
|
||||||
|
RegionInfo hri = rs.getRegions(TABLE_NAME).get(0).getRegionInfo();
|
||||||
|
TransitRegionStateProcedure assignRegionProcedure = TransitRegionStateProcedure.move(
|
||||||
|
UTIL.getMiniHBaseCluster().getMaster().getMasterProcedureExecutor().getEnvironment(),
|
||||||
|
hri, null);
|
||||||
|
RegionStateNode regionNode = UTIL.getMiniHBaseCluster().getMaster().getAssignmentManager()
|
||||||
|
.getRegionStates().getOrCreateRegionStateNode(hri);
|
||||||
|
regionNode.setProcedure(assignRegionProcedure);
|
||||||
|
countDownLatch.countDown();
|
||||||
|
long prodId = procedureExecutor.submitProcedure(assignRegionProcedure);
|
||||||
|
ProcedureTestingUtility.waitProcedure(procedureExecutor, prodId);
|
||||||
|
|
||||||
|
Assert.assertEquals("Should be two RS since other is aborted", 2,
|
||||||
|
UTIL.getMiniHBaseCluster().getLiveRegionServerThreads().size());
|
||||||
|
Assert.assertNull("RIT Map doesn't have correct value",
|
||||||
|
getRegionServer(0).getRegionsInTransitionInRS().get(hri.getEncodedNameAsBytes()));
|
||||||
|
Assert.assertNull("RIT Map doesn't have correct value",
|
||||||
|
getRegionServer(1).getRegionsInTransitionInRS().get(hri.getEncodedNameAsBytes()));
|
||||||
|
Assert.assertNull("RIT Map doesn't have correct value",
|
||||||
|
getRegionServer(2).getRegionsInTransitionInRS().get(hri.getEncodedNameAsBytes()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private HRegionServer getRegionServer(int index) {
|
||||||
|
return UTIL.getMiniHBaseCluster().getRegionServer(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class ThrowInOpenCP implements RegionCoprocessor, RegionObserver {
|
||||||
|
@Override public void preOpen(ObserverContext<RegionCoprocessorEnvironment> c) {
|
||||||
|
if (countDownLatch.getCount() == 1) {
|
||||||
|
// We want to throw exception only first time in move region call
|
||||||
|
// After that RS aborts and we don't want to throw in any other open region
|
||||||
|
countDownLatch.countDown();
|
||||||
|
throw new RuntimeException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<RegionObserver> getRegionObserver() {
|
||||||
|
return Optional.of(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,124 @@
|
||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.hadoop.hbase.master.assignment;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
import org.apache.hadoop.hbase.HBaseClassTestRule;
|
||||||
|
import org.apache.hadoop.hbase.HBaseTestingUtility;
|
||||||
|
import org.apache.hadoop.hbase.TableName;
|
||||||
|
import org.apache.hadoop.hbase.client.RegionInfo;
|
||||||
|
import org.apache.hadoop.hbase.coprocessor.CoprocessorHost;
|
||||||
|
import org.apache.hadoop.hbase.coprocessor.ObserverContext;
|
||||||
|
import org.apache.hadoop.hbase.coprocessor.RegionCoprocessor;
|
||||||
|
import org.apache.hadoop.hbase.coprocessor.RegionCoprocessorEnvironment;
|
||||||
|
import org.apache.hadoop.hbase.coprocessor.RegionObserver;
|
||||||
|
import org.apache.hadoop.hbase.procedure2.ProcedureExecutor;
|
||||||
|
import org.apache.hadoop.hbase.procedure2.ProcedureTestingUtility;
|
||||||
|
import org.apache.hadoop.hbase.regionserver.HRegionServer;
|
||||||
|
import org.apache.hadoop.hbase.testclassification.MasterTests;
|
||||||
|
import org.apache.hadoop.hbase.testclassification.MediumTests;
|
||||||
|
import org.apache.hadoop.hbase.util.Bytes;
|
||||||
|
import org.apache.hadoop.hbase.util.JVMClusterUtil;
|
||||||
|
import org.junit.AfterClass;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.BeforeClass;
|
||||||
|
import org.junit.ClassRule;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.experimental.categories.Category;
|
||||||
|
|
||||||
|
@Category({ MasterTests.class, MediumTests.class })
|
||||||
|
public class TestExceptionInUnassignedRegion {
|
||||||
|
|
||||||
|
@ClassRule
|
||||||
|
public static final HBaseClassTestRule CLASS_RULE =
|
||||||
|
HBaseClassTestRule.forClass(TestExceptionInUnassignedRegion.class);
|
||||||
|
|
||||||
|
private static final HBaseTestingUtility UTIL = new HBaseTestingUtility();
|
||||||
|
|
||||||
|
private static final TableName TABLE_NAME = TableName.valueOf("test");
|
||||||
|
|
||||||
|
private static final byte[] CF = Bytes.toBytes("cf");
|
||||||
|
|
||||||
|
@BeforeClass
|
||||||
|
public static void setUp() throws Exception {
|
||||||
|
UTIL.getConfiguration().setStrings(CoprocessorHost.REGION_COPROCESSOR_CONF_KEY,
|
||||||
|
ThrowInCloseCP.class.getName());
|
||||||
|
UTIL.startMiniCluster(3);
|
||||||
|
UTIL.getAdmin().balancerSwitch(false, true);
|
||||||
|
UTIL.createTable(TABLE_NAME, CF);
|
||||||
|
UTIL.waitTableAvailable(TABLE_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterClass
|
||||||
|
public static void tearDown() throws Exception {
|
||||||
|
UTIL.shutdownMiniCluster();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testExceptionInUnassignRegion() {
|
||||||
|
ProcedureExecutor procedureExecutor =
|
||||||
|
UTIL.getMiniHBaseCluster().getMaster().getMasterProcedureExecutor();
|
||||||
|
|
||||||
|
JVMClusterUtil.RegionServerThread rsThread = null;
|
||||||
|
for (JVMClusterUtil.RegionServerThread t : UTIL.getMiniHBaseCluster()
|
||||||
|
.getRegionServerThreads()) {
|
||||||
|
if (!t.getRegionServer().getRegions(TABLE_NAME).isEmpty()) {
|
||||||
|
rsThread = t;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// find the rs and hri of the table
|
||||||
|
HRegionServer rs = rsThread.getRegionServer();
|
||||||
|
RegionInfo hri = rs.getRegions(TABLE_NAME).get(0).getRegionInfo();
|
||||||
|
TransitRegionStateProcedure moveRegionProcedure = TransitRegionStateProcedure.reopen(
|
||||||
|
UTIL.getMiniHBaseCluster().getMaster().getMasterProcedureExecutor().getEnvironment(), hri);
|
||||||
|
RegionStateNode regionNode = UTIL.getMiniHBaseCluster().getMaster().getAssignmentManager()
|
||||||
|
.getRegionStates().getOrCreateRegionStateNode(hri);
|
||||||
|
regionNode.setProcedure(moveRegionProcedure);
|
||||||
|
long prodId = procedureExecutor.submitProcedure(moveRegionProcedure);
|
||||||
|
ProcedureTestingUtility.waitProcedure(procedureExecutor, prodId);
|
||||||
|
|
||||||
|
Assert.assertEquals("Should be two RS since other is aborted", 2,
|
||||||
|
UTIL.getMiniHBaseCluster().getLiveRegionServerThreads().size());
|
||||||
|
Assert.assertNull("RIT Map doesn't have correct value",
|
||||||
|
getRegionServer(0).getRegionsInTransitionInRS().get(hri.getEncodedNameAsBytes()));
|
||||||
|
Assert.assertNull("RIT Map doesn't have correct value",
|
||||||
|
getRegionServer(1).getRegionsInTransitionInRS().get(hri.getEncodedNameAsBytes()));
|
||||||
|
Assert.assertNull("RIT Map doesn't have correct value",
|
||||||
|
getRegionServer(2).getRegionsInTransitionInRS().get(hri.getEncodedNameAsBytes()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private HRegionServer getRegionServer(int index) {
|
||||||
|
return UTIL.getMiniHBaseCluster().getRegionServer(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class ThrowInCloseCP implements RegionCoprocessor, RegionObserver {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void preClose(ObserverContext<RegionCoprocessorEnvironment> c, boolean abortRequested) {
|
||||||
|
if (!c.getEnvironment().getRegion().getRegionInfo().getTable().isSystemTable()) {
|
||||||
|
throw new RuntimeException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<RegionObserver> getRegionObserver() {
|
||||||
|
return Optional.of(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue