From b360caf697caf9d729276c32fbb1808df2fc90ac Mon Sep 17 00:00:00 2001 From: Wellington Chevreuil Date: Mon, 1 Apr 2019 15:49:39 +0100 Subject: [PATCH] HBASE-22143 - HBCK2 setRegionState command --- .../src/main/java/org/apache/hbase/HBCK2.java | 63 +++++++++++++++++++ .../test/java/org/apache/hbase/TestHBCK2.java | 59 ++++++++++++++++- .../hbase/TestHBCKCommandLineParsing.java | 7 +++ 3 files changed, 128 insertions(+), 1 deletion(-) diff --git a/hbase-hbck2/src/main/java/org/apache/hbase/HBCK2.java b/hbase-hbck2/src/main/java/org/apache/hbase/HBCK2.java index e9089e8..d055edf 100644 --- a/hbase-hbck2/src/main/java/org/apache/hbase/HBCK2.java +++ b/hbase-hbck2/src/main/java/org/apache/hbase/HBCK2.java @@ -27,6 +27,7 @@ import org.apache.commons.cli.ParseException; 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.ClusterConnection; 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,33 @@ public class HBCK2 extends Configured implements Tool { } } + RegionState.State setRegionState(String region, RegionState.State newState) + throws IOException { + if(newState==null){ + throw new IllegalArgumentException(); + } + RegionState.State currentState = null; + try(Connection connection = ConnectionFactory.createConnection(getConf())){ + 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); + 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); + } else { + System.out.println("ERROR: Could not find region " + region + " in meta."); + } + } + return currentState; + } + List assigns(String [] args) throws IOException { Options options = new Options(); Option override = Option.builder("o").longOpt("override").build(); @@ -272,6 +309,23 @@ public class HBCK2 extends Configured implements Tool { writer.println(" $ HBCK2 setTableState users ENABLED"); writer.println(" Returns whatever the previous table state was."); writer.println(); + writer.println(" " + SET_REGION_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 +457,15 @@ public class HBCK2 extends Configured implements Tool { 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; + } + System.out.println(setRegionState(commands[1], RegionState.State.valueOf(commands[2]))); + break; + default: usage(options, "Unsupported command: " + command); return EXIT_FAILURE; diff --git a/hbase-hbck2/src/test/java/org/apache/hbase/TestHBCK2.java b/hbase-hbck2/src/test/java/org/apache/hbase/TestHBCK2.java index e902275..16ce12b 100644 --- a/hbase-hbck2/src/test/java/org/apache/hbase/TestHBCK2.java +++ b/hbase-hbck2/src/test/java/org/apache/hbase/TestHBCK2.java @@ -19,9 +19,12 @@ package org.apache.hbase; import junit.framework.TestCase; import org.apache.hadoop.hbase.HBaseTestingUtility; +import org.apache.hadoop.hbase.HConstants; +import org.apache.hadoop.hbase.MetaTableAccessor; import org.apache.hadoop.hbase.TableName; import org.apache.hadoop.hbase.client.Admin; import org.apache.hadoop.hbase.client.RegionInfo; +import org.apache.hadoop.hbase.client.Result; import org.apache.hadoop.hbase.client.TableState; import org.apache.hadoop.hbase.master.RegionState; import org.apache.hadoop.hbase.util.Bytes; @@ -29,7 +32,6 @@ 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 +40,7 @@ import java.util.List; 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 +50,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 +137,58 @@ public class TestHBCK2 { } } + @Test + public void testSetRegionState() throws IOException { + TEST_UTIL.createTable(REGION_STATES_TABLE_NAME, Bytes.toBytes("family1")); + try (Admin admin = TEST_UTIL.getConnection().getAdmin()) { + List 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()); + RegionState.State state = hbck.setRegionState("NO_REGION", RegionState.State.CLOSING); + assertNull("Passed invalid region name should return state as null", state); + } + + @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 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{ + Result result = MetaTableAccessor.getRegionResult(TEST_UTIL.getConnection(), + regionInfo.getRegionName()); + byte[] currentStateValue = result.getValue(HConstants.CATALOG_FAMILY, + HConstants.STATE_QUALIFIER); + return RegionState.State.valueOf(Bytes.toString(currentStateValue)); + } + private void waitOnPids(List pids) { for (Long pid: pids) { while (!TEST_UTIL.getHBaseCluster().getMaster().getMasterProcedureExecutor(). diff --git a/hbase-hbck2/src/test/java/org/apache/hbase/TestHBCKCommandLineParsing.java b/hbase-hbck2/src/test/java/org/apache/hbase/TestHBCKCommandLineParsing.java index e8dccb8..2499611 100644 --- a/hbase-hbck2/src/test/java/org/apache/hbase/TestHBCKCommandLineParsing.java +++ b/hbase-hbck2/src/test/java/org/apache/hbase/TestHBCKCommandLineParsing.java @@ -59,5 +59,12 @@ public class TestHBCKCommandLineParsing { // 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"}); + } } -- 2.17.2 (Apple Git-113)