Return-Path: X-Original-To: apmail-hbase-commits-archive@www.apache.org Delivered-To: apmail-hbase-commits-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id BC39A10990 for ; Mon, 23 Dec 2013 06:47:09 +0000 (UTC) Received: (qmail 57378 invoked by uid 500); 23 Dec 2013 06:47:07 -0000 Delivered-To: apmail-hbase-commits-archive@hbase.apache.org Received: (qmail 57340 invoked by uid 500); 23 Dec 2013 06:47:06 -0000 Mailing-List: contact commits-help@hbase.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@hbase.apache.org Delivered-To: mailing list commits@hbase.apache.org Received: (qmail 57333 invoked by uid 99); 23 Dec 2013 06:47:06 -0000 Received: from nike.apache.org (HELO nike.apache.org) (192.87.106.230) by apache.org (qpsmtpd/0.29) with ESMTP; Mon, 23 Dec 2013 06:47:06 +0000 X-ASF-Spam-Status: No, hits=-2000.0 required=5.0 tests=ALL_TRUSTED X-Spam-Check-By: apache.org Received: from [140.211.11.4] (HELO eris.apache.org) (140.211.11.4) by apache.org (qpsmtpd/0.29) with ESMTP; Mon, 23 Dec 2013 06:47:02 +0000 Received: from eris.apache.org (localhost [127.0.0.1]) by eris.apache.org (Postfix) with ESMTP id 6CC5023888E2; Mon, 23 Dec 2013 06:46:41 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r1553078 - /hbase/branches/0.94/src/main/java/org/apache/hadoop/hbase/util/HBaseFsck.java Date: Mon, 23 Dec 2013 06:46:41 -0000 To: commits@hbase.apache.org From: anoopsamjohn@apache.org X-Mailer: svnmailer-1.0.9 Message-Id: <20131223064641.6CC5023888E2@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org Author: anoopsamjohn Date: Mon Dec 23 06:46:41 2013 New Revision: 1553078 URL: http://svn.apache.org/r1553078 Log: HBASE-9346 HBCK should provide an option to check if regions boundaries are the same in META and in stores. (Jean Marc) Modified: hbase/branches/0.94/src/main/java/org/apache/hadoop/hbase/util/HBaseFsck.java Modified: hbase/branches/0.94/src/main/java/org/apache/hadoop/hbase/util/HBaseFsck.java URL: http://svn.apache.org/viewvc/hbase/branches/0.94/src/main/java/org/apache/hadoop/hbase/util/HBaseFsck.java?rev=1553078&r1=1553077&r2=1553078&view=diff ============================================================================== --- hbase/branches/0.94/src/main/java/org/apache/hadoop/hbase/util/HBaseFsck.java (original) +++ hbase/branches/0.94/src/main/java/org/apache/hadoop/hbase/util/HBaseFsck.java Mon Dec 23 06:46:41 2013 @@ -88,6 +88,7 @@ import org.apache.hadoop.hbase.regionser import org.apache.hadoop.hbase.regionserver.StoreFile; import org.apache.hadoop.hbase.regionserver.wal.HLog; import org.apache.hadoop.hbase.security.User; +import org.apache.hadoop.hbase.util.Bytes.ByteArrayComparator; import org.apache.hadoop.hbase.util.HBaseFsck.ErrorReporter.ERROR_CODE; import org.apache.hadoop.hbase.util.hbck.HFileCorruptionChecker; import org.apache.hadoop.hbase.util.hbck.TableIntegrityErrorHandler; @@ -201,6 +202,7 @@ public class HBaseFsck extends Configure 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 boolean checkMetaOnly = false; + private boolean checkRegionBoundaries = false; private boolean ignorePreCheckPermission = false; // if pre-check permission /********* @@ -451,6 +453,10 @@ public class HBaseFsck extends Configure admin.setBalancerRunning(oldBalancer, false); } + if (checkRegionBoundaries) { + checkRegionBoundaries(); + } + offlineReferenceFileRepair(); // Print table summary @@ -458,6 +464,112 @@ public class HBaseFsck extends Configure return errors.summarize(); } + public static byte[] keyOnly (byte[] b) { + if (b == null) + return b; + int rowlength = Bytes.toShort(b, 0); + byte[] result = new byte[rowlength]; + System.arraycopy(b, Bytes.SIZEOF_SHORT, result, 0, rowlength); + return result; + } + + private static class RegionBoundariesInformation { + public byte [] regionName; + public byte [] metaFirstKey; + public byte [] metaLastKey; + public byte [] storesFirstKey; + public byte [] storesLastKey; + public String toString () { + return "regionName=" + Bytes.toStringBinary(regionName) + + "\nmetaFirstKey=" + Bytes.toStringBinary(metaFirstKey) + + "\nmetaLastKey=" + Bytes.toStringBinary(metaLastKey) + + "\nstoresFirstKey=" + Bytes.toStringBinary(storesFirstKey) + + "\nstoresLastKey=" + Bytes.toStringBinary(storesLastKey); + } + } + + public void checkRegionBoundaries() { + try { + ByteArrayComparator comparator = new ByteArrayComparator(); + List regions = MetaScanner.listAllRegions(getConf(), false); + final RegionBoundariesInformation currentRegionBoundariesInformation = + new RegionBoundariesInformation(); + for (HRegionInfo regionInfo : regions) { + currentRegionBoundariesInformation.regionName = regionInfo.getRegionName(); + // For each region, get the start and stop key from the META and compare them to the + // same information from the Stores. + Path path = new Path(getConf().get(HConstants.HBASE_DIR) + "/" + + Bytes.toString(regionInfo.getTableName()) + "/" + + regionInfo.getEncodedName() + "/"); + FileSystem fs = path.getFileSystem(getConf()); + FileStatus[] files = fs.listStatus(path); + // For all the column families in this region... + byte[] storeFirstKey = null; + byte[] storeLastKey = null; + for (FileStatus file : files) { + String fileName = file.getPath().toString(); + fileName = fileName.substring(fileName.lastIndexOf("/") + 1); + if (!fileName.startsWith(".") && !fileName.endsWith("recovered.edits")) { + FileStatus[] storeFiles = fs.listStatus(file.getPath()); + // For all the stores in this column family. + for (FileStatus storeFile : storeFiles) { + HFile.Reader reader = HFile.createReader(fs, storeFile.getPath(), new CacheConfig( + getConf())); + if ((reader.getFirstKey() != null) + && ((storeFirstKey == null) || (comparator.compare(storeFirstKey, + reader.getFirstKey()) > 0))) { + storeFirstKey = reader.getFirstKey(); + } + if ((reader.getLastKey() != null) + && ((storeLastKey == null) || (comparator.compare(storeLastKey, + reader.getLastKey())) < 0)) { + storeLastKey = reader.getLastKey(); + } + reader.close(); + } + } + } + currentRegionBoundariesInformation.metaFirstKey = regionInfo.getStartKey(); + currentRegionBoundariesInformation.metaLastKey = regionInfo.getEndKey(); + currentRegionBoundariesInformation.storesFirstKey = keyOnly(storeFirstKey); + currentRegionBoundariesInformation.storesLastKey = keyOnly(storeLastKey); + if (currentRegionBoundariesInformation.metaFirstKey.length == 0) + currentRegionBoundariesInformation.metaFirstKey = null; + if (currentRegionBoundariesInformation.metaLastKey.length == 0) + currentRegionBoundariesInformation.metaLastKey = null; + + // For a region to be correct, we need the META start key to be smaller or equal to the + // smallest start key from all the stores, and the start key from the next META entry to + // be bigger than the last key from all the current stores. First region start key is null; + // Last region end key is null; some regions can be empty and not have any store. + + boolean valid = true; + // Checking start key. + if ((currentRegionBoundariesInformation.storesFirstKey != null) + && (currentRegionBoundariesInformation.metaFirstKey != null)) { + valid = valid + && comparator.compare(currentRegionBoundariesInformation.storesFirstKey, + currentRegionBoundariesInformation.metaFirstKey) >= 0; + } + // Checking stop key. + if ((currentRegionBoundariesInformation.storesLastKey != null) + && (currentRegionBoundariesInformation.metaLastKey != null)) { + valid = valid + && comparator.compare(currentRegionBoundariesInformation.storesLastKey, + currentRegionBoundariesInformation.metaLastKey) < 0; + } + if (!valid) { + errors.reportError(ERROR_CODE.BOUNDARIES_ERROR, "Found issues with regions boundaries", + tablesInfo.get(Bytes.toString(regionInfo.getTableName()))); + LOG.warn("Region's boundaries not alligned between stores and META for:"); + LOG.warn(currentRegionBoundariesInformation); + } + } + } catch (IOException e) { + LOG.error(e); + } + } + /** * Iterates through the list of all orphan/invalid regiondirs. */ @@ -2898,7 +3010,7 @@ public class HBaseFsck extends Configure FIRST_REGION_STARTKEY_NOT_EMPTY, LAST_REGION_ENDKEY_NOT_EMPTY, DUPE_STARTKEYS, HOLE_IN_REGION_CHAIN, OVERLAP_IN_REGION_CHAIN, REGION_CYCLE, DEGENERATE_REGION, ORPHAN_HDFS_REGION, LINGERING_SPLIT_PARENT, NO_TABLEINFO_FILE, LINGERING_REFERENCE_HFILE, - WRONG_USAGE + WRONG_USAGE, BOUNDARIES_ERROR } public void clear(); public void report(String message); @@ -3415,6 +3527,13 @@ public class HBaseFsck extends Configure this.hfcc = hfcc; } + /** + * Set region boundaries check mode. + */ + void setRegionBoundariesCheck() { + checkRegionBoundaries = true; + } + public void setRetCode(int code) { this.retcode = code; } @@ -3457,6 +3576,7 @@ public class HBaseFsck extends Configure out.println(" -fixSplitParents Try to force offline split parents to be online."); out.println(" -ignorePreCheckPermission ignore filesystem permission pre-check"); out.println(" -fixReferenceFiles Try to offline lingering reference store files"); + out.println(" -boundaries Verify that regions boundaries are the same between META and store files."); out.println(""); out.println(" Datafile Repair options: (expert features, use with caution!)"); @@ -3639,6 +3759,8 @@ public class HBaseFsck extends Configure setSummary(); } else if (cmd.equals("-metaonly")) { setCheckMetaOnly(); + } else if (cmd.equals("-boundaries")) { + setRegionBoundariesCheck(); } else if (cmd.startsWith("-")) { errors.reportError(ERROR_CODE.WRONG_USAGE, "Unrecognized option:" + cmd); return printUsageAndExit();