HBASE-23117: Bad enum in hbase:meta info:state column can fail loadMeta and stop startup (#867)

* Handling the BAD value in info:state columns in hbase:meta

* Adding a unit test and region encoded name in the LOG

* Adding a null check for region state to complete the test scenario and fixing the nit

Signed-off-by: Wellington Chevreuil <wchevreuil@apache.org>
Signed-off-by: GuangxuCheng  <guangxucheng@gmail.com>
Signed-off-by: stack <stack@apache.org>
This commit is contained in:
Sandeep Pal 2019-11-26 10:10:01 -08:00 committed by stack
parent b99f58304e
commit 0d7a6b9725
3 changed files with 53 additions and 5 deletions

View File

@ -132,7 +132,7 @@ public class RegionStateStore {
if (regionInfo == null) continue; if (regionInfo == null) continue;
final int replicaId = regionInfo.getReplicaId(); final int replicaId = regionInfo.getReplicaId();
final State state = getRegionState(result, replicaId); final State state = getRegionState(result, replicaId, regionInfo);
final ServerName lastHost = hrl.getServerName(); final ServerName lastHost = hrl.getServerName();
final ServerName regionLocation = getRegionServer(result, replicaId); final ServerName regionLocation = getRegionServer(result, replicaId);
@ -347,13 +347,22 @@ public class RegionStateStore {
* @return the region state, or null if unknown. * @return the region state, or null if unknown.
*/ */
@VisibleForTesting @VisibleForTesting
public static State getRegionState(final Result r, int replicaId) { public static State getRegionState(final Result r, int replicaId, RegionInfo regionInfo) {
Cell cell = r.getColumnLatestCell(HConstants.CATALOG_FAMILY, getStateColumn(replicaId)); Cell cell = r.getColumnLatestCell(HConstants.CATALOG_FAMILY, getStateColumn(replicaId));
if (cell == null || cell.getValueLength() == 0) { if (cell == null || cell.getValueLength() == 0) {
return null; return null;
} }
return State.valueOf(Bytes.toString(cell.getValueArray(), cell.getValueOffset(),
cell.getValueLength())); String state = Bytes.toString(cell.getValueArray(), cell.getValueOffset(),
cell.getValueLength());
try {
return State.valueOf(state);
} catch (IllegalArgumentException e) {
LOG.warn("BAD value {} in hbase:meta info:state column for region {} , " +
"Consider using HBCK2 setRegionState ENCODED_REGION_NAME STATE",
state, regionInfo.getEncodedName());
return null;
}
} }
private static byte[] getStateColumn(int replicaId) { private static byte[] getStateColumn(int replicaId) {

View File

@ -3606,7 +3606,7 @@ public class HBaseTestingUtility extends HBaseZKTestingUtility {
} }
} }
if (RegionStateStore.getRegionState(r, if (RegionStateStore.getRegionState(r,
info.getReplicaId()) != RegionState.State.OPEN) { info.getReplicaId(), info) != RegionState.State.OPEN) {
return false; return false;
} }
} }

View File

@ -19,20 +19,26 @@ package org.apache.hadoop.hbase.master.assignment;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import java.util.List; import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import org.apache.hadoop.hbase.HBaseClassTestRule; import org.apache.hadoop.hbase.HBaseClassTestRule;
import org.apache.hadoop.hbase.HBaseTestingUtility; import org.apache.hadoop.hbase.HBaseTestingUtility;
import org.apache.hadoop.hbase.HConstants;
import org.apache.hadoop.hbase.ServerName; import org.apache.hadoop.hbase.ServerName;
import org.apache.hadoop.hbase.TableName; import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.client.Put;
import org.apache.hadoop.hbase.client.RegionInfo; import org.apache.hadoop.hbase.client.RegionInfo;
import org.apache.hadoop.hbase.client.Result; import org.apache.hadoop.hbase.client.Result;
import org.apache.hadoop.hbase.client.Table;
import org.apache.hadoop.hbase.master.RegionState; import org.apache.hadoop.hbase.master.RegionState;
import org.apache.hadoop.hbase.regionserver.HRegion; import org.apache.hadoop.hbase.regionserver.HRegion;
import org.apache.hadoop.hbase.testclassification.MasterTests; import org.apache.hadoop.hbase.testclassification.MasterTests;
import org.apache.hadoop.hbase.testclassification.MediumTests; import org.apache.hadoop.hbase.testclassification.MediumTests;
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.ClassRule; import org.junit.ClassRule;
@ -84,6 +90,39 @@ public class TestRegionStateStore {
assertTrue("Visitor has not been called.", visitorCalled.get()); assertTrue("Visitor has not been called.", visitorCalled.get());
} }
@Test
public void testVisitMetaForBadRegionState() throws Exception {
final TableName tableName = TableName.valueOf("testVisitMetaForBadRegionState");
util.createTable(tableName, "cf");
final List<HRegion> regions = util.getHBaseCluster().getRegions(tableName);
final String encodedName = regions.get(0).getRegionInfo().getEncodedName();
final RegionStateStore regionStateStore = util.getHBaseCluster().getMaster().
getAssignmentManager().getRegionStateStore();
// add the BAD_STATE which does not exist in enum RegionState.State
Put put = new Put(regions.get(0).getRegionInfo().getRegionName(),
EnvironmentEdgeManager.currentTime());
put.addColumn(HConstants.CATALOG_FAMILY, HConstants.STATE_QUALIFIER,
Bytes.toBytes("BAD_STATE"));
try (Table table = util.getConnection().getTable(TableName.META_TABLE_NAME)) {
table.put(put);
}
final AtomicBoolean visitorCalled = new AtomicBoolean(false);
regionStateStore.visitMetaForRegion(encodedName, new RegionStateStore.RegionStateVisitor() {
@Override
public void visitRegionState(Result result, RegionInfo regionInfo,
RegionState.State state, ServerName regionLocation,
ServerName lastHost, long openSeqNum) {
assertEquals(encodedName, regionInfo.getEncodedName());
assertNull(state);
visitorCalled.set(true);
}
});
assertTrue("Visitor has not been called.", visitorCalled.get());
}
@Test @Test
public void testVisitMetaForRegionNonExistingRegion() throws Exception { public void testVisitMetaForRegionNonExistingRegion() throws Exception {
final String encodedName = "fakeencodedregionname"; final String encodedName = "fakeencodedregionname";