From 2cdb563712e74b3ef5e37304c4e4d2c6ccc37f0b 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 | 68 +++++++++++++++++++ .../test/java/org/apache/hbase/TestHBCK2.java | 65 +++++++++++++++++- .../hbase/TestHBCKCommandLineParsing.java | 7 ++ 3 files changed, 139 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..681e513 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,40 @@ public class HBCK2 extends Configured implements Tool { } } + 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 assigns(String [] args) throws IOException { Options options = new Options(); Option override = Option.builder("o").longOpt("override").build(); @@ -272,6 +316,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 +464,13 @@ 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; + } + return setRegionState(commands[1], RegionState.State.valueOf(commands[2])); 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..39c9c5b 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,14 @@ 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.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; @@ -29,7 +34,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 +42,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 +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 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()); + 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 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 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)