From 8fcf4e8371c84e2d5512fdefe3c04decfbdcbe03 Mon Sep 17 00:00:00 2001 From: Clara Xiong Date: Wed, 17 Jun 2020 22:17:44 -0700 Subject: [PATCH] HBASE-23927 hbck2 command should accept one or more files containing a list of region names --- hbase-hbck2/README.md | 6 +- .../src/main/java/org/apache/hbase/HBCK2.java | 69 +++++++++++-------- .../test/java/org/apache/hbase/TestHBCK2.java | 63 +++++++++++------ 3 files changed, 89 insertions(+), 49 deletions(-) diff --git a/hbase-hbck2/README.md b/hbase-hbck2/README.md index e7c01ffce4..4ba7f5ac84 100644 --- a/hbase-hbck2/README.md +++ b/hbase-hbck2/README.md @@ -137,12 +137,13 @@ Command: Returns the pid(s) of the created AssignProcedure(s) or -1 if none. If -i or --inputFiles is specified, pass one or more input file names. Each file contains encoded region names, one per line. For example: - $ HBCK2 assigns -i fileName1 fileName2 + $ HBCK2 -i assigns fileName1 fileName2 bypass [OPTIONS] ... Options: -o,--override override if procedure is running/stuck -r,--recursive bypass parent and its children. SLOW! EXPENSIVE! -w,--lockWait milliseconds to wait before giving up; default=1 + -i, --inputFile take one or more files to read the args from Pass one (or more) procedure 'pid's to skip to procedure finish. Parent of bypassed procedure will also be skipped to the finish. Entities will be left in an inconsistent state and will require manual fixup. May @@ -280,6 +281,9 @@ Command: of what a userspace encoded region name looks like. For example: $ HBCK2 unassign 1588230740 de00010733901a05f5a2a3a382e27dd4 Returns the pid(s) of the created UnassignProcedure(s) or -1 if none. + If -i or --inputFiles is specified, pass one or more input file names. + Each file contains encoded region names, one per line. For example: + $ HBCK2 -i unassigns fileName1 fileName2 SEE ALSO, org.apache.hbase.hbck1.OfflineMetaRepair, the offline hbase:meta tool. See the HBCK2 README for how to use. 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 4ab1b4b12a..56c6b23d00 100644 --- a/hbase-hbck2/src/main/java/org/apache/hbase/HBCK2.java +++ b/hbase-hbck2/src/main/java/org/apache/hbase/HBCK2.java @@ -114,6 +114,7 @@ public class HBCK2 extends Configured implements org.apache.hadoop.util.Tool { private Configuration conf; static final String [] MINIMUM_HBCK2_VERSION = {"2.0.3", "2.1.1", "2.2.0", "3.0.0"}; private boolean skipCheck = false; + private boolean getFromFile = false; /** * Wait 1ms on lock by default. @@ -203,8 +204,8 @@ Map> reportTablesWithMissingRegionsInMeta(String... nameSpa Map> report; try (final FsRegionsMetaRecoverer fsRegionsMetaRecoverer = new FsRegionsMetaRecoverer(this.conf)) { - report = fsRegionsMetaRecoverer.reportTablesMissingRegions( - formatNameSpaceTableParam(nameSpaceOrTable)); + report = fsRegionsMetaRecoverer.reportTablesMissingRegions(getFromArgsOrFiles( + formatNameSpaceTableParam(nameSpaceOrTable))); } catch (IOException e) { LOG.error("Error reporting missing regions: ", e); throw e; @@ -228,7 +229,7 @@ Map> extraRegionsInMeta(String[] args) Map> result = new HashMap<>(); try (final FsRegionsMetaRecoverer fsRegionsMetaRecoverer = new FsRegionsMetaRecoverer(this.conf)) { - List namespacesTables = formatNameSpaceTableParam(commandLine.getArgs()); + List namespacesTables = getFromArgsOrFiles(formatNameSpaceTableParam(commandLine.getArgs())); Map> reportMap = fsRegionsMetaRecoverer.reportTablesExtraRegions(namespacesTables); final List toFix = new ArrayList<>(); @@ -265,16 +266,12 @@ Map> extraRegionsInMeta(String[] args) return result; } - private List formatNameSpaceTableParam(String... nameSpaceOrTable) { - return nameSpaceOrTable != null ? Arrays.asList(nameSpaceOrTable) : null; - } - List>> addMissingRegionsInMetaForTables(String... nameSpaceOrTable) throws IOException { try (final FsRegionsMetaRecoverer fsRegionsMetaRecoverer = new FsRegionsMetaRecoverer(this.conf)) { - return fsRegionsMetaRecoverer.addMissingRegionsInMetaForTables( - formatNameSpaceTableParam(nameSpaceOrTable)); + return fsRegionsMetaRecoverer.addMissingRegionsInMetaForTables(getFromArgsOrFiles( + formatNameSpaceTableParam(nameSpaceOrTable))); } catch (IOException e) { LOG.error("Error adding missing regions: ", e); throw e; @@ -284,9 +281,7 @@ List>> addMissingRegionsInMetaForTables(String... List assigns(Hbck hbck, String[] args) throws IOException { Options options = new Options(); Option override = Option.builder("o").longOpt("override").build(); - Option inputFile = Option.builder("i").longOpt("inputFiles").build(); options.addOption(override); - options.addOption(inputFile); // Parse command-line. CommandLineParser parser = new DefaultParser(); CommandLine commandLine; @@ -297,21 +292,8 @@ List assigns(Hbck hbck, String[] args) throws IOException { return null; } boolean overrideFlag = commandLine.hasOption(override.getOpt()); - List argList = commandLine.getArgList(); - if (!commandLine.hasOption(inputFile.getOpt())) { - return hbck.assigns(argList, overrideFlag); - } - List assignmentList = new ArrayList<>(); - for (String filePath : argList) { - try (InputStream fileStream = new FileInputStream(filePath)){ - LineIterator it = IOUtils.lineIterator(fileStream, "UTF-8"); - while (it.hasNext()) { - assignmentList.add(it.nextLine().trim()); - } - } - } - return hbck.assigns(assignmentList, overrideFlag); + return hbck.assigns(this.getFromArgsOrFiles(argList), overrideFlag); } List unassigns(Hbck hbck, String [] args) throws IOException { @@ -328,7 +310,30 @@ List unassigns(Hbck hbck, String [] args) throws IOException { return null; } boolean overrideFlag = commandLine.hasOption(override.getOpt()); - return hbck.unassigns(commandLine.getArgList(), overrideFlag); + return hbck.unassigns(getFromArgsOrFiles(commandLine.getArgList()), overrideFlag); + } + + private List formatNameSpaceTableParam(String... nameSpaceOrTable) { + return nameSpaceOrTable != null ? Arrays.asList(nameSpaceOrTable) : null; + } + + /** + * @return Read arguments from a list of input files + */ + private List getFromArgsOrFiles(List args) throws IOException { + if (!getFromFile || args == null) { + return args; + } + List argList = new ArrayList<>(); + for (String filePath : args) { + try (InputStream fileStream = new FileInputStream(filePath)){ + LineIterator it = IOUtils.lineIterator(fileStream, "UTF-8"); + while (it.hasNext()) { + argList.add(it.nextLine().trim()); + } + } + } + return argList; } /** @@ -461,7 +466,6 @@ private static void usageAssigns(PrintWriter writer) { writer.println(" " + ASSIGNS + " [OPTIONS] ..."); writer.println(" Options:"); writer.println(" -o,--override override ownership by another procedure"); - writer.println(" -i,--inputFiles take one or more files of encoded region names"); writer.println(" A 'raw' assign that can be used even during Master initialization (if"); writer.println(" the -skip flag is specified). Skirts Coprocessors. Pass one or more"); writer.println(" encoded region names. 1588230740 is the hard-coded name for the"); @@ -471,7 +475,7 @@ private static void usageAssigns(PrintWriter writer) { writer.println(" Returns the pid(s) of the created AssignProcedure(s) or -1 if none."); writer.println(" If -i or --inputFiles is specified, pass one or more input file names."); writer.println(" Each file contains encoded region names, one per line. For example:"); - writer.println(" $ HBCK2 assigns -i fileName1 fileName2"); + writer.println(" $ HBCK2 -i assigns fileName1 fileName2"); } private static void usageBypass(PrintWriter writer) { @@ -641,6 +645,9 @@ private static void usageUnassigns(PrintWriter writer) { writer.println(" of what a userspace encoded region name looks like. For example:"); writer.println(" $ HBCK2 unassigns 1588230740 de00010733901a05f5a2a3a382e27dd4"); writer.println(" Returns the pid(s) of the created UnassignProcedure(s) or -1 if none."); + writer.println(" If -i or --inputFiles is specified, pass one or more input file names."); + writer.println(" Each file contains encoded region names, one per line. For example:"); + writer.println(" $ HBCK2 -i unassigns fileName1 fileName2"); writer.println(); writer.println(" SEE ALSO, org.apache.hbase.hbck1.OfflineMetaRepair, the offline"); writer.println(" hbase:meta tool. See the HBCK2 README for how to use."); @@ -695,6 +702,9 @@ public int run(String[] args) throws IOException { Option skip = Option.builder("s").longOpt("skip"). desc("skip hbase version check (PleaseHoldException)").build(); options.addOption(skip); + Option inputFile = Option.builder("i").longOpt("inputFiles") + .desc("take one or more files to read the args from").build(); + options.addOption(inputFile); // Parse command-line. CommandLineParser parser = new DefaultParser(); @@ -745,6 +755,9 @@ public int run(String[] args) throws IOException { if (commandLine.hasOption(skip.getOpt())) { skipCheck = true; } + if (commandLine.hasOption(inputFile.getOpt())) { + getFromFile = true; + } return doCommandLine(commandLine, options); } 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 1e8ba04686..9b8e78f677 100644 --- a/hbase-hbck2/src/test/java/org/apache/hbase/TestHBCK2.java +++ b/hbase-hbck2/src/test/java/org/apache/hbase/TestHBCK2.java @@ -52,7 +52,6 @@ import org.apache.hadoop.hbase.master.RegionState; import org.apache.hadoop.hbase.util.Bytes; import org.apache.hadoop.hbase.util.CommonFSUtils; -import org.apache.hadoop.hbase.util.FSUtils; import org.apache.hadoop.hbase.util.Threads; import org.junit.After; @@ -77,6 +76,7 @@ public class TestHBCK2 { valueOf(TestHBCK2.class.getSimpleName() + "-REGIONS_STATES"); private final static String ASSIGNS = "assigns"; private static final String EXTRA_REGIONS_IN_META = "extraRegionsInMeta"; + private final static String UNASSIGNS = "unassigns"; @Rule public TestName testName = new TestName(); @@ -125,6 +125,24 @@ public void testSetTableStateInMeta() throws IOException { } } + @Test + public void testUnAssigns() throws IOException { + try (Admin admin = TEST_UTIL.getConnection().getAdmin()) { + List regions = admin.getRegions(TABLE_NAME); + for (RegionInfo ri : regions) { + RegionState rs = TEST_UTIL.getHBaseCluster().getMaster().getAssignmentManager(). + getRegionStates().getRegionState(ri.getEncodedName()); + LOG.info("RS: {}", rs.toString()); + } + String[] regionStrsArray = + regions.stream().map(RegionInfo::getEncodedName).toArray(String[]::new); + File testFile = new File(TEST_UTIL.getDataTestDir().toString(), "inputForUnAssignsTest"); + writeStringsToAFile(testFile, regionStrsArray); + String result = testRunWithArgs(new String[]{"-i", UNASSIGNS, testFile.toString()}); + validateRegionEndState(getPidsFromResult(result), regions, false); + } + } + @Test public void testAssigns() throws IOException { try (Admin admin = TEST_UTIL.getConnection().getAdmin()) { @@ -140,8 +158,7 @@ public void testAssigns() throws IOException { try (ClusterConnection connection = this.hbck2.connect(); Hbck hbck = connection.getHbck()) { unassigns(regions, regionStrsArray); List pids = this.hbck2.assigns(hbck, regionStrsArray); - waitOnPids(pids); - validateOpen(regions); + validateRegionEndState(pids,regions, true); // What happens if crappy region list passed? pids = this.hbck2.assigns(hbck, Arrays.stream(new String[]{"a", "some rubbish name"}). collect(Collectors.toList()).toArray(new String[]{})); @@ -152,20 +169,9 @@ public void testAssigns() throws IOException { // test input files unassigns(regions, regionStrsArray); File testFile = new File(TEST_UTIL.getDataTestDir().toString(), "inputForAssignsTest"); - try (FileOutputStream output = new FileOutputStream(testFile, false)) { - for (String regionStr : regionStrsArray) { - output.write((regionStr + System.lineSeparator()).getBytes()); - } - } - String result = testRunWithArgs(new String[]{ASSIGNS, "-i", testFile.toString()}); - Scanner scanner = new Scanner(result).useDelimiter("[\\D]+"); - pids = new ArrayList<>(); - while (scanner.hasNext()) { - pids.add(scanner.nextLong()); - } - scanner.close(); - waitOnPids(pids); - validateOpen(regions); + writeStringsToAFile(testFile, regionStrsArray); + String result = testRunWithArgs(new String[]{"-i", ASSIGNS, testFile.toString()}); + validateRegionEndState(getPidsFromResult(result), regions, true); } } } @@ -279,6 +285,23 @@ public void testFormatReportMissingInMetaOneMissing() throws IOException { assertTrue(result.contains(expectedResult)); } + private void writeStringsToAFile(File testFile, String[] strs) throws IOException { + try (FileOutputStream output = new FileOutputStream(testFile, false)) { + for (String regionStr : strs) { + output.write((regionStr + System.lineSeparator()).getBytes()); + } + } + } + private List getPidsFromResult(String result) { + Scanner scanner = new Scanner(result).useDelimiter("[\\D]+"); + List pids = new ArrayList<>(); + while (scanner.hasNext()) { + pids.add(scanner.nextLong()); + } + scanner.close(); + return pids; + } + private void unassigns(List regions, String[] regionStrsArray) throws IOException { try (ClusterConnection connection = this.hbck2.connect(); Hbck hbck = connection.getHbck()) { List pids = this.hbck2.unassigns(hbck, regionStrsArray); @@ -292,13 +315,13 @@ private void unassigns(List regions, String[] regionStrsArray) throw } } - - private void validateOpen(List regions) { + private void validateRegionEndState(List pids, List regions, boolean open) { + waitOnPids(pids); for (RegionInfo ri : regions) { RegionState rs = TEST_UTIL.getHBaseCluster().getMaster().getAssignmentManager(). getRegionStates().getRegionState(ri.getEncodedName()); LOG.info("RS: {}", rs.toString()); - assertTrue(rs.toString(), rs.isOpened()); + assertTrue(rs.toString(), open? rs.isOpened() : rs.isClosed()); } }