You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hbase.apache.org by ec...@apache.org on 2014/08/01 00:07:59 UTC
[23/50] [abbrv] git commit: [HBASE-11379] added fixRegionInfo method
to rewrite stale .regioninfo files
[HBASE-11379] added fixRegionInfo method to rewrite stale .regioninfo files
Summary: added method in HBaseFsck to rewrite .regioninfo files that are inconsistent with .META
Test Plan: intentionally corrupted .regioninfo files and then fixed them
Reviewers: manukranthk
Reviewed By: manukranthk
Subscribers: adela, hbase-eng@
Differential Revision: https://phabricator.fb.com/D1389834
Tasks: 4490604
git-svn-id: svn+ssh://tubbs/svnhive/hadoop/branches/titan/VENDOR.hbase/hbase-trunk@42987 e7acf4d4-3532-417f-9e73-7a9ae25a1f51
Project: http://git-wip-us.apache.org/repos/asf/hbase/repo
Commit: http://git-wip-us.apache.org/repos/asf/hbase/commit/d8ba4507
Tree: http://git-wip-us.apache.org/repos/asf/hbase/tree/d8ba4507
Diff: http://git-wip-us.apache.org/repos/asf/hbase/diff/d8ba4507
Branch: refs/heads/0.89-fb
Commit: d8ba4507b6ad798f049432c790ff8945b7256d3c
Parents: cd54a01
Author: yuq <yu...@e7acf4d4-3532-417f-9e73-7a9ae25a1f51>
Authored: Thu Jun 19 19:24:16 2014 +0000
Committer: Elliott Clark <el...@fb.com>
Committed: Thu Jul 31 14:44:23 2014 -0700
----------------------------------------------------------------------
.../apache/hadoop/hbase/client/HBaseFsck.java | 169 ++++++++++++------
.../client/TestHBaseFsckFixRegionInfo.java | 178 +++++++++++++++++++
2 files changed, 288 insertions(+), 59 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/hbase/blob/d8ba4507/src/main/java/org/apache/hadoop/hbase/client/HBaseFsck.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/hadoop/hbase/client/HBaseFsck.java b/src/main/java/org/apache/hadoop/hbase/client/HBaseFsck.java
index cab5e01..5206f4a 100644
--- a/src/main/java/org/apache/hadoop/hbase/client/HBaseFsck.java
+++ b/src/main/java/org/apache/hadoop/hbase/client/HBaseFsck.java
@@ -46,6 +46,7 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FSDataInputStream;
+import org.apache.hadoop.fs.FSDataOutputStream;
import org.apache.hadoop.fs.FileStatus;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
@@ -79,7 +80,7 @@ import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
/**
- * Check consistency among the in-memory states of the master and the
+ * Check and enforce consistency among the in-memory states of the master and the
* region server(s) and the state of data in HDFS.
*/
public class HBaseFsck {
@@ -104,6 +105,7 @@ public class HBaseFsck {
private boolean rerun = false; // if we tried to fix something rerun hbck
private static boolean summary = false; // if we want to print less output
private static boolean checkRegionInfo = false;
+ private static boolean fixRegionInfo = false;
private static boolean promptResponse = false; // "no" to all prompt questions
private int numThreads = MAX_NUM_THREADS;
private final String UnkownTable = "__UnknownTable__";
@@ -128,7 +130,7 @@ public class HBaseFsck {
* @throws MasterNotRunningException if the master is not running
*/
public HBaseFsck(Configuration conf)
- throws MasterNotRunningException, IOException {
+ throws MasterNotRunningException, IOException {
this.conf = conf;
// fetch information from master
@@ -138,8 +140,8 @@ public class HBaseFsck {
numThreads = conf.getInt("hbasefsck.numthreads", numThreads);
executor = new ThreadPoolExecutor(numThreads, numThreads,
- THREADS_KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
- new LinkedBlockingQueue<Runnable>());
+ THREADS_KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
+ new LinkedBlockingQueue<Runnable>());
executor.allowCoreThreadTimeOut(true);
}
@@ -195,17 +197,17 @@ public class HBaseFsck {
for (HTableDescriptor td : allTables) {
String tableName = td.getNameAsString();
errors.detail("\t Table: " + tableName + "\t" +
- (td.isReadOnly() ? "ro" : "rw") + "\t" +
- (td.isRootRegion() ? "ROOT" :
- (td.isMetaRegion() ? "META" : " ")) + "\t" +
- " families:" + td.getFamilies().size());
+ (td.isReadOnly() ? "ro" : "rw") + "\t" +
+ (td.isRootRegion() ? "ROOT" :
+ (td.isMetaRegion() ? "META" : " ")) + "\t" +
+ " families:" + td.getFamilies().size());
}
}
// From the master, get a list of all known live region servers
Collection<HServerInfo> regionServers = status.getServerInfo();
errors.print("Number of live region servers:" +
- regionServers.size());
+ regionServers.size());
if (details) {
for (HServerInfo rsinfo: regionServers) {
errors.detail("\t RegionServer:" + rsinfo.getServerName());
@@ -215,7 +217,7 @@ public class HBaseFsck {
// From the master, get a list of all dead region servers
Collection<String> deadRegionServers = status.getDeadServerNames();
errors.print("Number of dead region servers:" +
- deadRegionServers.size());
+ deadRegionServers.size());
if (details) {
for (String name: deadRegionServers) {
errors.detail("\t RegionServer(dead):" + name);
@@ -242,6 +244,11 @@ public class HBaseFsck {
checkRegionInfo();
}
+ //Rewrite .regioninfo files that are inconsistent with .META
+ if (fixRegionInfo){
+ fixRegionInfo();
+ }
+
// Print table summary
printTableSummary();
@@ -252,9 +259,11 @@ public class HBaseFsck {
* Read the .regioninfo for all regions of the table and compare with the
* corresponding entry in .META.
* Entry in .regioninfo should be consistent with the entry in .META.
- *
+ * @return Map<HRegionInfo, Path> containing the HRegionInfo objects corresponding to
+ * HRegions whose .regioninfo files should be rewritten and the Paths to be written at
*/
- void checkRegionInfo() {
+ Map<HRegionInfo, Path> checkRegionInfo() {
+ Map<HRegionInfo, Path> risToRewrite = new TreeMap<HRegionInfo, Path>();
Path tableDir = null;
try {
@@ -266,10 +275,11 @@ public class HBaseFsck {
FileSystem fs = rootDir.getFileSystem(conf);
Path regionPath = HRegion.getRegionDir(tableDir,
- hbi.metaEntry.getEncodedName());
+ hbi.metaEntry.getEncodedName());
Path regionInfoPath = new Path(regionPath, HRegion.REGIONINFO_FILE);
+
if (fs.exists(regionInfoPath) &&
- fs.getFileStatus(regionInfoPath).getLen() > 0) {
+ fs.getFileStatus(regionInfoPath).getLen() > 0) {
FSDataInputStream in = fs.open(regionInfoPath);
HRegionInfo f_hri = null;
try {
@@ -277,7 +287,7 @@ public class HBaseFsck {
f_hri.readFields(in);
} catch (IOException ex) {
errors.reportError("Could not read .regioninfo file at "
- + regionInfoPath);
+ + regionInfoPath);
} finally {
in.close();
}
@@ -285,23 +295,60 @@ public class HBaseFsck {
HRegionInfo m_hri = hbckinfo.metaEntry;
if(!f_hri.equals(m_hri)) {
errors.reportError("Table name: " +
- f_hri.getTableDesc().getNameAsString() +
- " RegionInfo for "+ f_hri.getRegionNameAsString() +
- " inconsistent in .META. and .regioninfo");
+ f_hri.getTableDesc().getNameAsString() +
+ " RegionInfo for "+ f_hri.getRegionNameAsString() +
+ " inconsistent in .META. and .regioninfo");
+ risToRewrite.put(m_hri, regionInfoPath);
}
} else {
if (!fs.exists(regionInfoPath)) {
errors.reportError(".regioninfo not found at "
- + regionInfoPath.toString());
+ + regionInfoPath.toString());
} else if (fs.getFileStatus(regionInfoPath).getLen() <= 0) {
errors.reportError(".regioninfo file is empty (path = "
- + regionInfoPath + ")");
+ + regionInfoPath + ")");
}
}
}
} catch (IOException e) {
errors.reportError("Error in comparing .regioninfo and .META."
- + e.getMessage());
+ + e.getMessage());
+ }
+
+ return risToRewrite;
+ }
+
+ /**
+ * Read the .regioninfo for all regions of the table and compare with the
+ * corresponding entry in .META.
+ * Entry in .regioninfo should be consistent with the entry in .META.
+ * If .regioninfo is not consistent with .META, then we rewrite the .regioninfo
+ * @throws IOException
+ */
+ void fixRegionInfo() throws IOException {
+ Map<HRegionInfo, Path> risToRewrite = checkRegionInfo();
+ LOG.info("FOUND " + risToRewrite.size() + " corrupted .regioninfo files to rewrite");
+ Path rootDir = new Path(conf.get(HConstants.HBASE_DIR));
+ FileSystem fs = rootDir.getFileSystem(conf);
+ for(Map.Entry<HRegionInfo, Path> entry : risToRewrite.entrySet()){
+ HRegionInfo hri = entry.getKey();
+ Path regionInfoPath = entry.getValue();
+ FSDataOutputStream out = fs.create(regionInfoPath, true);
+
+ try {
+ hri.write(out);
+ out.write('\n');
+ out.write('\n');
+ out.write(Bytes.toBytes(hri.toString()));
+ LOG.debug("Rewrote .regioninfo file of " + Bytes.toString(hri.getRegionName()) +"at " + regionInfoPath);
+ } catch (IOException e){
+ errors.reportError("Could not rewrite the .regioninfo of "+Bytes.toString(hri.getRegionName()) + " at " +regionInfoPath);
+ }
+
+ finally {
+ out.close();
+ LOG.debug(".regioninfo File Contents: " + Bytes.toBytes(hri.toString()));
+ }
}
}
@@ -347,7 +394,7 @@ public class HBaseFsck {
*/
boolean recordRootRegion() throws IOException {
HRegionLocation rootLocation = connection.locateRegion(
- HConstants.ROOT_TABLE_NAME_STRINGBYTES, HConstants.EMPTY_START_ROW);
+ HConstants.ROOT_TABLE_NAME_STRINGBYTES, HConstants.EMPTY_START_ROW);
// Check if Root region is valid and existing
if (rootLocation == null || rootLocation.getRegionInfo() == null ||
@@ -357,7 +404,7 @@ public class HBaseFsck {
}
MetaEntry m = new MetaEntry(rootLocation.getRegionInfo(),
- rootLocation.getServerAddress(), null, System.currentTimeMillis());
+ rootLocation.getServerAddress(), null, System.currentTimeMillis());
HbckInfo hbInfo = new HbckInfo(m);
regionInfo.put(rootLocation.getRegionInfo().getEncodedName(), hbInfo);
return true;
@@ -430,13 +477,13 @@ public class HBaseFsck {
boolean isDeployed = !hbi.deployedOn.isEmpty();
boolean isMultiplyDeployed = hbi.deployedOn.size() > 1;
boolean deploymentMatchesMeta =
- hasMetaAssignment && isDeployed && !isMultiplyDeployed &&
- hbi.metaEntry.regionServer.equals(hbi.deployedOn.get(0));
+ hasMetaAssignment && isDeployed && !isMultiplyDeployed &&
+ hbi.metaEntry.regionServer.equals(hbi.deployedOn.get(0));
boolean shouldBeDeployed = inMeta && !hbi.metaEntry.isOffline();
long tooRecent = System.currentTimeMillis() - timelag;
boolean recentlyModified =
- (inHdfs && hbi.foundRegionDir.getModificationTime() > tooRecent) ||
- (inMeta && hbi.metaEntry.modTime > tooRecent);
+ (inHdfs && hbi.foundRegionDir.getModificationTime() > tooRecent) ||
+ (inMeta && hbi.metaEntry.modTime > tooRecent);
String tableName = null;
if (inMeta) {
@@ -478,23 +525,23 @@ public class HBaseFsck {
assert false : "Entry for region with no data";
} else if (!inMeta && !inHdfs && isDeployed) {
String message = "Region " + descriptiveName + " not on HDFS or in META but " +
- "deployed on " + Joiner.on(", ").join(hbi.deployedOn);
+ "deployed on " + Joiner.on(", ").join(hbi.deployedOn);
errors.reportError(message);
tableInfo.addRegionDetails(RegionType.missing, descriptiveName);
tableInfo.addRegionError(message);
} else if (!inMeta && inHdfs && !isDeployed) {
String message = "Region " + descriptiveName + " on HDFS, but not listed in META " +
- "or deployed on any region server.";
+ "or deployed on any region server.";
errors.reportError(message);
tableInfo.addRegionDetails(RegionType.missing, descriptiveName);
tableInfo.addRegionError(message);
} else if (!inMeta && inHdfs && isDeployed) {
String message = "Region " + descriptiveName + " not in META, but deployed on " +
- Joiner.on(", ").join(hbi.deployedOn);
+ Joiner.on(", ").join(hbi.deployedOn);
errors.reportError(message);
tableInfo.addRegionDetails(RegionType.missing, descriptiveName);
tableInfo.addRegionError(message);
- // ========== Cases where the region is in META =============
+ // ========== Cases where the region is in META =============
} else if (inMeta && !inHdfs && !isDeployed) {
String message = "Region " + descriptiveName + " found in META, but not in HDFS " +
"or deployed on any region server.";
@@ -503,7 +550,7 @@ public class HBaseFsck {
tableInfo.addRegionError(message);
} else if (inMeta && !inHdfs && isDeployed) {
String message = "Region " + descriptiveName + " found in META, but not in HDFS, " +
- "and deployed on " + Joiner.on(", ").join(hbi.deployedOn);
+ "and deployed on " + Joiner.on(", ").join(hbi.deployedOn);
errors.reportError(message);
tableInfo.addRegionDetails(RegionType.unknown, descriptiveName);
tableInfo.addRegionError(message);
@@ -720,7 +767,7 @@ public class HBaseFsck {
if (Bytes.compareTo(prevEndKey, curStartKey) != 0) {
this.holesFound++;
errorSB.append("Found hole in table from start key "
- + posToStr(prevEndKey) + " to end key " + posToStr(curStartKey) + "\n");
+ + posToStr(prevEndKey) + " to end key " + posToStr(curStartKey) + "\n");
}
visited.add(curStartKey);
@@ -730,8 +777,8 @@ public class HBaseFsck {
if (visited.contains(prevEndKey)
&& Bytes.compareTo(prevEndKey, new byte[0]) != 0) {
errorSB.append("Cycle found in region chain. "
- + "Current Start Key "+ posToStr(curStartKey)
- + "; Cycle Start Key " + posToStr(prevEndKey) + "\n");
+ + "Current Start Key "+ posToStr(curStartKey)
+ + "; Cycle Start Key " + posToStr(prevEndKey) + "\n");
}
}
@@ -739,7 +786,7 @@ public class HBaseFsck {
if (Bytes.compareTo(prevEndKey, new byte[0]) != 0) {
this.holesFound++;
errorSB.append("Found hole in table from start key "
- + posToStr(prevEndKey) + " to end of table (end key 0)\n");
+ + posToStr(prevEndKey) + " to end of table (end key 0)\n");
}
if (this.holesFound > 0) {
@@ -854,10 +901,10 @@ public class HBaseFsck {
}
/**
- * Get values in regionInfo for .META.
- *
- * @return list of meta regions
- */
+ * Get values in regionInfo for .META.
+ *
+ * @return list of meta regions
+ */
private List<HbckInfo> getMetaRegions() {
List<HbckInfo> metaRegions = Lists.newArrayList();
for (HbckInfo value : regionInfo.values()) {
@@ -894,7 +941,7 @@ public class HBaseFsck {
// record region details
byte[] value = result.getValue(HConstants.CATALOG_FAMILY,
- HConstants.REGIONINFO_QUALIFIER);
+ HConstants.REGIONINFO_QUALIFIER);
HRegionInfo info = null;
HServerAddress server = null;
byte[] startCode = null;
@@ -904,7 +951,7 @@ public class HBaseFsck {
// record assigned region server
value = result.getValue(HConstants.CATALOG_FAMILY,
- HConstants.SERVER_QUALIFIER);
+ HConstants.SERVER_QUALIFIER);
if (value != null && value.length > 0) {
String address = Bytes.toString(value);
server = new HServerAddress(address);
@@ -912,7 +959,7 @@ public class HBaseFsck {
// record region's start key
value = result.getValue(HConstants.CATALOG_FAMILY,
- HConstants.STARTCODE_QUALIFIER);
+ HConstants.STARTCODE_QUALIFIER);
if (value != null) {
startCode = value;
}
@@ -999,7 +1046,7 @@ public class HBaseFsck {
long modTime; // timestamp of most recent modification metadata
public MetaEntry(HRegionInfo rinfo, HServerAddress regionServer,
- byte[] startCode, long modTime) {
+ byte[] startCode, long modTime) {
super(rinfo);
this.regionServer = regionServer;
this.modTime = modTime;
@@ -1108,9 +1155,9 @@ public class HBaseFsck {
public synchronized int summarize() {
if (HBaseFsck.json == null) {
System.out.println(Integer.toString(errorCount + fixableCount) +
- " inconsistencies detected.");
+ " inconsistencies detected.");
System.out.println(Integer.toString(fixableCount) +
- " inconsistencies are fixable.");
+ " inconsistencies are fixable.");
}
if (warnCount > 0) {
if (HBaseFsck.json == null) {
@@ -1181,7 +1228,7 @@ public class HBaseFsck {
private boolean done;
WorkItemRegion(HBaseFsck hbck, HServerInfo info,
- ErrorReporter errors, HConnection connection) {
+ ErrorReporter errors, HConnection connection) {
this.hbck = hbck;
this.rsinfo = info;
this.errors = errors;
@@ -1200,7 +1247,7 @@ public class HBaseFsck {
errors.progress();
try {
HRegionInterface server = connection.getHRegionConnection(
- rsinfo.getServerAddress());
+ rsinfo.getServerAddress());
// list all online regions from this region server
HRegionInfo[] regions = server.getRegionsAssignment();
@@ -1208,13 +1255,13 @@ public class HBaseFsck {
if (details) {
StringBuffer buf = new StringBuffer();
buf.append("\nRegionServer:" + rsinfo.getServerName() +
- " number of regions:" + regions.length);
+ " number of regions:" + regions.length);
for (HRegionInfo rinfo: regions) {
buf.append("\n\t name:" + rinfo.getRegionNameAsString() +
- " id:" + rinfo.getRegionId() +
- " encoded name:" + rinfo.getEncodedName() +
- " start :" + Bytes.toStringBinary(rinfo.getStartKey()) +
- " end :" + Bytes.toStringBinary(rinfo.getEndKey()));
+ " id:" + rinfo.getRegionId() +
+ " encoded name:" + rinfo.getEncodedName() +
+ " start :" + Bytes.toStringBinary(rinfo.getStartKey()) +
+ " end :" + Bytes.toStringBinary(rinfo.getEndKey()));
}
errors.detail(buf.toString());
}
@@ -1226,7 +1273,7 @@ public class HBaseFsck {
}
} catch (IOException e) { // unable to connect to the region server.
errors.reportWarning("RegionServer: " + rsinfo.getServerName()
- + " Unable to fetch region information. " + e);
+ + " Unable to fetch region information. " + e);
hbck.addFailedServer(rsinfo.getServerAddress());
} finally {
done = true;
@@ -1246,7 +1293,7 @@ public class HBaseFsck {
private boolean done;
WorkItemHdfsDir(HBaseFsck hbck, FileSystem fs, ErrorReporter errors,
- FileStatus status) {
+ FileStatus status) {
this.hbck = hbck;
this.fs = fs;
this.tableDir = status;
@@ -1279,7 +1326,7 @@ public class HBaseFsck {
synchronized (hbi) {
if (hbi.foundRegionDir != null) {
errors.print("Directory " + encodedName + " duplicate??" +
- hbi.foundRegionDir);
+ hbi.foundRegionDir);
}
hbi.foundRegionDir = regionDir;
@@ -1299,7 +1346,7 @@ public class HBaseFsck {
}
} catch (IOException e) { // unable to connect to the region server.
errors.reportError("Table Directory: " + tableDir.getPath().getName() +
- " Unable to fetch region information. " + e);
+ " Unable to fetch region information. " + e);
} finally {
done = true;
notifyAll();
@@ -1395,7 +1442,7 @@ public class HBaseFsck {
* @throws ParseException
*/
public static void main(String [] args)
- throws IOException,
+ throws IOException,
MasterNotRunningException, InterruptedException, ParseException {
Options opt = new Options();
@@ -1422,6 +1469,7 @@ public class HBaseFsck {
opt.addOption("summary", false, "Print only summary of the tables and status.");
opt.addOption("detail", false, "Display full report of all regions.");
opt.addOption("checkRegionInfo", false, "Check if .regioninfo is consistent with .META.");
+ opt.addOption("fixRegionInfo", false, "Rewrite all .regioninfo that are inconsistent with .META");
opt.addOption("h", false, "Display this help");
opt.addOption("json", false, "Outputs the table summaries and errors in JSON format");
opt.addOption("forceCleanMeta", false,
@@ -1492,11 +1540,14 @@ public class HBaseFsck {
HBaseFsck.setPromptResponse(true);
}
if (cmd.hasOption("summary")) {
- fsck.setSummary();
+ fsck.setSummary();
}
if (cmd.hasOption("checkRegionInfo")) {
checkRegionInfo = true;
}
+ if (cmd.hasOption("fixRegionInfo")){
+ fixRegionInfo = true;
+ }
if (cmd.hasOption("json")) {
fsck.setJsonFlag("json");
}
http://git-wip-us.apache.org/repos/asf/hbase/blob/d8ba4507/src/test/java/org/apache/hadoop/hbase/client/TestHBaseFsckFixRegionInfo.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/hadoop/hbase/client/TestHBaseFsckFixRegionInfo.java b/src/test/java/org/apache/hadoop/hbase/client/TestHBaseFsckFixRegionInfo.java
new file mode 100644
index 0000000..44738aa
--- /dev/null
+++ b/src/test/java/org/apache/hadoop/hbase/client/TestHBaseFsckFixRegionInfo.java
@@ -0,0 +1,178 @@
+/**
+ * Copyright 2009 The Apache Software Foundation
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.hadoop.hbase.client;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.fs.FSDataInputStream;
+import org.apache.hadoop.fs.FSDataOutputStream;
+import org.apache.hadoop.fs.FileSystem;
+import org.apache.hadoop.fs.Path;
+import org.apache.hadoop.hbase.HBaseTestingUtility;
+import org.apache.hadoop.hbase.HColumnDescriptor;
+import org.apache.hadoop.hbase.HConstants;
+import org.apache.hadoop.hbase.HRegionInfo;
+import org.apache.hadoop.hbase.HTableDescriptor;
+import org.apache.hadoop.hbase.MediumTests;
+import org.apache.hadoop.hbase.client.HBaseFsck.HbckInfo;
+import org.apache.hadoop.hbase.regionserver.HRegion;
+import org.apache.hadoop.hbase.util.Bytes;
+import org.apache.hadoop.hbase.util.FSUtils;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+
+import java.io.IOException;
+import java.util.Map;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Class to test the checkRegionInfo() and fixRegionInfo() methods of HBaseFsck.
+ */
+@Category(MediumTests.class)
+public class TestHBaseFsckFixRegionInfo {
+ final Log LOG = LogFactory.getLog(getClass());
+ private final static HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
+ private HBaseAdmin admin;
+ private static final int NUM_REGION_SERVER = 3;
+
+ @BeforeClass
+ public static void setUpBeforeClass() throws Exception {
+ TEST_UTIL.getConfiguration().setInt("hbase.regionserver.msginterval", 100);
+ TEST_UTIL.getConfiguration().setInt("hbase.client.pause", 250);
+ TEST_UTIL.getConfiguration().setInt("hbase.client.retries.number", 6);
+ TEST_UTIL.startMiniCluster(NUM_REGION_SERVER);
+ }
+
+ @AfterClass
+ public static void tearDownAfterClass() throws Exception {
+ TEST_UTIL.shutdownMiniCluster();
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ this.admin = new HBaseAdmin(TEST_UTIL.getConfiguration());
+ }
+
+ @Test
+ public void testFixRegionInfo() throws IOException, InterruptedException {
+
+ byte[] tableName = Bytes.toBytes("testFixRegionInfo");
+
+ byte[][] splitKeys = {
+ new byte[] { 1, 1, 1 },
+ new byte[] { 2, 2, 2 },
+ new byte[] { 3, 3, 3 },
+ new byte[] { 4, 4, 4 },
+ new byte[] { 5, 5, 5 },
+ new byte[] { 6, 6, 6 },
+ new byte[] { 7, 7, 7 },
+ new byte[] { 8, 8, 8 },
+ new byte[] { 9, 9, 9 },
+ };
+
+ HTableDescriptor desc = new HTableDescriptor(tableName);
+ desc.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY));
+ admin.createTable(desc, splitKeys);
+
+ Configuration conf = TEST_UTIL.getConfiguration();
+ HBaseFsck fsckToCorrupt = new HBaseFsck(TEST_UTIL.getConfiguration());
+ fsckToCorrupt.initAndScanRootMeta();
+ /*
+ * The following line is necessary because some .regioninfo files (specifically the ROOT and the META)
+ * become outdated immediately after initialization. This is because some parameters are immediately changed
+ * after initialization, and these changes are not updated in the .regioninfo. So here we update all files.
+ * This allows us to accurate measure the # of corrupted files later.
+ */
+ fsckToCorrupt.fixRegionInfo();
+
+ //Randomly corrupt some files
+ int modifyCount = 0; // # of corrupted files
+ for (HbckInfo hbi : fsckToCorrupt.getRegionInfo().values()) {
+ if (Math.random() < 0.5) {
+ Path tableDir = HTableDescriptor.getTableDir(FSUtils.getRootDir(conf),
+ hbi.metaEntry.getTableDesc().getName());
+ Path rootDir = new Path(conf.get(HConstants.HBASE_DIR));
+ FileSystem fs = rootDir.getFileSystem(conf);
+ Path regionPath = HRegion.getRegionDir(tableDir,
+ hbi.metaEntry.getEncodedName());
+ Path regionInfoPath = new Path(regionPath, HRegion.REGIONINFO_FILE);
+
+ //read the original .regioninfo into an HRegionInfo
+ FSDataInputStream in = fs.open(regionInfoPath);
+ HRegionInfo hri_original = new HRegionInfo();
+ hri_original.readFields(in);
+ in.close();
+
+ //change the HRegionInfo
+ HRegionInfo hri_modified = randomlyModifyRegion(hri_original);
+
+ //rewrite the original .regioninfo
+ FSDataOutputStream out = fs.create(regionInfoPath, true);
+ hri_modified.write(out);
+ out.write('\n');
+ out.write('\n');
+ out.write(Bytes.toBytes(hri_modified.toString()));
+ out.close();
+ modifyCount++;
+ }
+ }
+
+ //we incorrectly rewrote some .regioninfo files, so some .regioninfos are incorrect
+ HBaseFsck fsckCorrupted = new HBaseFsck(TEST_UTIL.getConfiguration());
+ fsckCorrupted.initAndScanRootMeta();
+ Map<HRegionInfo, Path> risToRewrite = fsckCorrupted.checkRegionInfo();
+ assertEquals(modifyCount, risToRewrite.size()); // # of files to rewrite must be the # of modified files
+
+ //after fixing, we should see no errors
+ HBaseFsck fsckFixed = new HBaseFsck(TEST_UTIL.getConfiguration());
+ fsckFixed.initAndScanRootMeta();
+ fsckFixed.fixRegionInfo();
+ risToRewrite = fsckFixed.checkRegionInfo();
+ assertEquals(0, risToRewrite.size());
+ }
+
+ HRegionInfo randomlyModifyRegion(HRegionInfo hri){
+ HTableDescriptor tableDesc = hri.getTableDesc();
+ double rand = Math.random();
+ if (rand < 0.2){
+ tableDesc.setReadOnly(!tableDesc.isReadOnly());
+ }
+ else if (rand < 0.4){
+ tableDesc.setDeferredLogFlush(!tableDesc.isDeferredLogFlush());
+ }
+ else if (rand < 0.6){
+ tableDesc.setName(Bytes.toBytes("NEWNAME"));
+ }
+ else if (rand < 0.8){
+ tableDesc.setMaxFileSize( (long) 100);
+ }
+ else{
+ tableDesc.setMemStoreFlushSize( (long) 200);
+ }
+ hri.setTableDesc(tableDesc);
+ return hri;
+ }
+
+}