Skip to content
Permalink
Browse files
HBASE-22143 - HBCK2 setRegionState command
Signed-off-by: Josh Elser <elserj@apache.org>
  • Loading branch information
Wellington Chevreuil authored and joshelser committed Apr 4, 2019
1 parent a7ea07b commit 5baf88d22f77b6a506c71c59598980fdb3189599
Showing 3 changed files with 139 additions and 1 deletion.
@@ -27,6 +27,7 @@
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.conf.Configured;
import org.apache.hadoop.hbase.ClusterMetrics;
import org.apache.hadoop.hbase.CompareOperator;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.HConstants;
import org.apache.hadoop.hbase.TableName;
@@ -35,7 +36,15 @@
import org.apache.hadoop.hbase.client.Connection;
import org.apache.hadoop.hbase.client.ConnectionFactory;
import org.apache.hadoop.hbase.client.Hbck;
import org.apache.hadoop.hbase.client.Put;
import org.apache.hadoop.hbase.client.Result;
import org.apache.hadoop.hbase.client.Scan;
import org.apache.hadoop.hbase.client.Table;
import org.apache.hadoop.hbase.client.TableState;
import org.apache.hadoop.hbase.filter.RowFilter;
import org.apache.hadoop.hbase.filter.SubstringComparator;
import org.apache.hadoop.hbase.master.RegionState;
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.hadoop.hbase.util.VersionInfo;
import org.apache.hadoop.util.Tool;
import org.apache.hadoop.util.ToolRunner;
@@ -77,6 +86,7 @@ public class HBCK2 extends Configured implements Tool {
private static final String UNASSIGNS = "unassigns";
private static final String BYPASS = "bypass";
private static final String VERSION = "version";
private static final String SET_REGION_STATE = "setRegionState";
private Configuration conf;
private static final String TWO_POINT_ONE = "2.1.0";
private static final String MININUM_VERSION = "2.0.3";
@@ -120,6 +130,40 @@ TableState setTableState(TableName tableName, TableState.State state) throws IOE
}
}

int setRegionState(String region, RegionState.State newState)
throws IOException {
if(newState==null){
throw new IllegalArgumentException("State can't be null.");
}
try(Connection connection = ConnectionFactory.createConnection(getConf())){
RegionState.State currentState = null;
Table table = connection.getTable(TableName.valueOf("hbase:meta"));
RowFilter filter = new RowFilter(CompareOperator.EQUAL, new SubstringComparator(region));
Scan scan = new Scan();
scan.setFilter(filter);
Result result = table.getScanner(scan).next();
if(result!=null){
byte[] currentStateValue = result.getValue(HConstants.CATALOG_FAMILY,
HConstants.STATE_QUALIFIER);
if(currentStateValue==null){
System.out.println("WARN: Region state info on meta was NULL");
}else {
currentState = RegionState.State.valueOf(Bytes.toString(currentStateValue));
}
Put put = new Put(result.getRow());
put.addColumn(HConstants.CATALOG_FAMILY, HConstants.STATE_QUALIFIER,
Bytes.toBytes(newState.name()));
table.put(put);
System.out.println("Changed region " + region + " STATE from "
+ currentState + " to " + newState);
return EXIT_SUCCESS;
} else {
System.out.println("ERROR: Could not find region " + region + " in meta.");
}
}
return EXIT_FAILURE;
}

List<Long> assigns(String [] args) throws IOException {
Options options = new Options();
Option override = Option.builder("o").longOpt("override").build();
@@ -272,6 +316,23 @@ private static final String getCommandUsage() {
writer.println(" $ HBCK2 setTableState users ENABLED");
writer.println(" Returns whatever the previous table state was.");
writer.println();
writer.println(" " + SET_REGION_STATE + " <ENCODED_REGIONNAME> <STATE>");
writer.println(" Possible region states: " + Arrays.stream(RegionState.State.values()).
map(i -> i.toString()).collect(Collectors.joining(", ")));
writer.println(" WARNING: This is a very risky option intended for use as last resource.");
writer.println(" Example scenarios for this is when unassings/assigns can't move forward ");
writer.println(" due region being on an inconsistent state in META. For example, ");
writer.println(" 'unassigns' command can only proceed ");
writer.println(" if passed in region is in one of following states: ");
writer.println(" [SPLITTING|SPLIT|MERGING|OPEN|CLOSING]");
writer.println(" Before manually setting a region state with this command,");
writer.println(" please certify that this region is not being handled by");
writer.println(" a running procedure, such as Assign or Split. You can get a view of ");
writer.println(" running procedures from hbase shell, using 'list_procedures' command. ");
writer.println(" An example setting region 'de00010733901a05f5a2a3a382e27dd4' to CLOSING:");
writer.println(" $ HBCK2 setRegionState de00010733901a05f5a2a3a382e27dd4 CLOSING");
writer.println(" Returns whatever the previous region state was.");
writer.println();

writer.close();
return sw.toString();
@@ -403,6 +464,13 @@ public int run(String[] args) throws IOException {
System.out.println(toString(unassigns(purgeFirst(commands))));
break;

case SET_REGION_STATE:
if(commands.length < 3){
usage(options, command + " takes region encoded name and state arguments: e.g. "
+ "35f30b0ce922c34bf5c284eff33ba8b3 CLOSING");
return EXIT_FAILURE;
}
return setRegionState(commands[1], RegionState.State.valueOf(commands[2]));
default:
usage(options, "Unsupported command: " + command);
return EXIT_FAILURE;
@@ -19,17 +19,21 @@

import junit.framework.TestCase;
import org.apache.hadoop.hbase.HBaseTestingUtility;
import org.apache.hadoop.hbase.HConstants;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.client.Admin;
import org.apache.hadoop.hbase.client.Delete;
import org.apache.hadoop.hbase.client.Get;
import org.apache.hadoop.hbase.client.RegionInfo;
import org.apache.hadoop.hbase.client.Result;
import org.apache.hadoop.hbase.client.Table;
import org.apache.hadoop.hbase.client.TableState;
import org.apache.hadoop.hbase.master.RegionState;
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.hadoop.hbase.util.Threads;
import org.apache.logging.log4j.LogManager;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;

import java.io.IOException;
@@ -38,6 +42,7 @@
import java.util.stream.Collectors;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;

/**
* Tests commands. For command-line parsing, see adjacent test.
@@ -47,6 +52,8 @@ public class TestHBCK2 {
private static final org.apache.logging.log4j.Logger LOG = LogManager.getLogger(TestHBCK2.class);
private final static HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
private static final TableName TABLE_NAME = TableName.valueOf(TestHBCK2.class.getSimpleName());
private static final TableName REGION_STATES_TABLE_NAME = TableName.
valueOf(TestHBCK2.class.getSimpleName() + "-REGIONS_STATES");

@BeforeClass
public static void beforeClass() throws Exception {
@@ -132,6 +139,62 @@ public void testAssigns() throws IOException {
}
}

@Test
public void testSetRegionState() throws IOException {
TEST_UTIL.createTable(REGION_STATES_TABLE_NAME, Bytes.toBytes("family1"));
try (Admin admin = TEST_UTIL.getConnection().getAdmin()) {
List<RegionInfo> regions = admin.getRegions(REGION_STATES_TABLE_NAME);
RegionInfo info = regions.get(0);
assertEquals(RegionState.State.OPEN, getCurrentRegionState(info));
HBCK2 hbck = new HBCK2(TEST_UTIL.getConfiguration());
String region = info.getEncodedName();
hbck.setRegionState(region, RegionState.State.CLOSING);
assertEquals(RegionState.State.CLOSING, getCurrentRegionState(info));
} finally {
TEST_UTIL.deleteTable(REGION_STATES_TABLE_NAME);
}
}

@Test
public void testSetRegionStateInvalidRegion() throws IOException {
HBCK2 hbck = new HBCK2(TEST_UTIL.getConfiguration());
assertEquals(HBCK2.EXIT_FAILURE, hbck.setRegionState("NO_REGION",
RegionState.State.CLOSING));
}

@Test (expected = IllegalArgumentException.class)
public void testSetRegionStateInvalidState() throws IOException {
TEST_UTIL.createTable(REGION_STATES_TABLE_NAME, Bytes.toBytes("family1"));
try (Admin admin = TEST_UTIL.getConnection().getAdmin()) {
List<RegionInfo> regions = admin.getRegions(REGION_STATES_TABLE_NAME);
RegionInfo info = regions.get(0);
assertEquals(RegionState.State.OPEN, getCurrentRegionState(info));
HBCK2 hbck = new HBCK2(TEST_UTIL.getConfiguration());
String region = info.getEncodedName();
hbck.setRegionState(region, null);
} finally {
TEST_UTIL.deleteTable(REGION_STATES_TABLE_NAME);
}
}

@Test (expected = IllegalArgumentException.class)
public void testSetRegionStateInvalidRegionAndInvalidState() throws IOException {
HBCK2 hbck = new HBCK2(TEST_UTIL.getConfiguration());
hbck.setRegionState("NO_REGION", null);
}

private RegionState.State getCurrentRegionState(RegionInfo regionInfo) throws IOException{
Table metaTable = TEST_UTIL.getConnection().getTable(TableName.valueOf("hbase:meta"));
Get get = new Get(regionInfo.getRegionName());
get.addColumn(HConstants.CATALOG_FAMILY, HConstants.STATE_QUALIFIER);
Result result = metaTable.get(get);
byte[] currentStateValue = result.getValue(HConstants.CATALOG_FAMILY,
HConstants.STATE_QUALIFIER);
return currentStateValue != null ?
RegionState.State.valueOf(Bytes.toString(currentStateValue))
: null;
}

private void waitOnPids(List<Long> pids) {
for (Long pid: pids) {
while (!TEST_UTIL.getHBaseCluster().getMaster().getMasterProcedureExecutor().
@@ -59,5 +59,12 @@ public void testCommandWithOptions() throws IOException {
// The 'x' below should cause the NumberFormatException. The Options should all be good.
hbck.run(new String[]{"bypass", "--lockWait=3", "--override", "--recursive", "x"});
}

@Test (expected=IllegalArgumentException.class)
public void testSetRegionStateCommandInvalidState() throws IOException {
HBCK2 hbck = new HBCK2(TEST_UTIL.getConfiguration());
// The 'x' below should cause the NumberFormatException. The Options should all be good.
hbck.run(new String[]{"setRegionState", "region_encoded", "INVALID_STATE"});
}
}

0 comments on commit 5baf88d

Please sign in to comment.