Index: src/test/java/org/apache/hadoop/hbase/util/hbck/OfflineVersionAndClusterIdFixTests.java =================================================================== --- src/test/java/org/apache/hadoop/hbase/util/hbck/OfflineVersionAndClusterIdFixTests.java (revision 0) +++ src/test/java/org/apache/hadoop/hbase/util/hbck/OfflineVersionAndClusterIdFixTests.java (revision 0) @@ -0,0 +1,72 @@ +/** + * 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.util.hbck; + +import java.io.File; +import java.net.URI; + +import junit.framework.Assert; + +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.hbase.HBaseConfiguration; +import org.apache.hadoop.hbase.HConstants; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +/** + * Validates offline fixes for hbase.version and hbase.id + * See https://issues.apache.org/jira/browse/HBASE-7191 + * + * Requires hbase.rootdir set + */ +public class OfflineVersionAndClusterIdFixTests { + + private static URI hbaseRootDirUri; + + @Test + public void validateVersionFileFix() throws Exception{ + OfflineMetaRepair.main(new String[]{"-fixVersionFile"}); + File file = new File(hbaseRootDirUri.getRawPath(), "hbase.version"); + Assert.assertTrue(file.exists()); + } + + @Test + public void validateClusterIdFileFix() throws Exception{ + OfflineMetaRepair.main(new String[]{"-fixClusterIdFile"}); + File file = new File(hbaseRootDirUri.getRawPath(), "hbase.id"); + Assert.assertTrue(file.exists()); + } + + @BeforeClass + public static void prepare() throws Exception { + Configuration conf = HBaseConfiguration.create(); + hbaseRootDirUri = new URI(conf.get(HConstants.HBASE_DIR)); + File file = new File(hbaseRootDirUri.getRawPath(), "hbase.version"); + Assert.assertFalse(file.exists()); + file = new File(hbaseRootDirUri.getRawPath(), "hbase.id"); + Assert.assertFalse(file.exists()); + } + + @AfterClass + public static void cleanup() throws Exception { + File file = new File(hbaseRootDirUri.getRawPath(), "hbase.version"); + file.delete(); + file = new File(hbaseRootDirUri.getRawPath(), "hbase.id"); + file.delete(); + } +} Index: src/test/resources/hbase-site.xml =================================================================== --- src/test/resources/hbase-site.xml (revision 1411749) +++ src/test/resources/hbase-site.xml (working copy) @@ -23,6 +23,14 @@ --> + hbase.rootdir + file:///tmp/hbase-${user.name}/hbase + + + hbase.zookeeper.property.dataDir + file:///tmp/hbase-${user.name}/zookeeper + + hbase.regionserver.msginterval 1000 Interval between messages from the RegionServer to HMaster Index: src/main/java/org/apache/hadoop/hbase/util/hbck/OfflineMetaRepair.java =================================================================== --- src/main/java/org/apache/hadoop/hbase/util/hbck/OfflineMetaRepair.java (revision 1411749) +++ src/main/java/org/apache/hadoop/hbase/util/hbck/OfflineMetaRepair.java (working copy) @@ -18,12 +18,18 @@ package org.apache.hadoop.hbase.util.hbck; import java.io.IOException; +import java.lang.reflect.Field; +import java.util.UUID; +import java.util.concurrent.ExecutorService; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.fs.FileSystem; +import org.apache.hadoop.fs.Path; import org.apache.hadoop.hbase.HBaseConfiguration; import org.apache.hadoop.hbase.HConstants; +import org.apache.hadoop.hbase.util.FSUtils; import org.apache.hadoop.hbase.util.HBaseFsck; import org.apache.hadoop.io.MultipleIOException; @@ -49,7 +55,9 @@ append(" -base Base Hbase Data directory.\n"). append(" -sidelineDir HDFS path to backup existing meta and root.\n"). append(" -fix Auto fix as many problems as possible.\n"). - append(" -fixHoles Auto fix as region holes."); + append(" -fixHoles Auto fix as region holes.\n"). + append(" -fixVersionFile Fixes corrupt hbase.version file.\n"). + append(" -fixClusterIdFile Fixes corrupt hbase.id file."); System.err.println(sb.toString()); Runtime.getRuntime().exit(-2); } @@ -66,8 +74,13 @@ Configuration conf = HBaseConfiguration.create(); // Cover both bases, the old way of setting default fs and the new. // We're supposed to run on 0.20 and 0.21 anyways. - conf.set("fs.defaultFS", conf.get(HConstants.HBASE_DIR)); - conf.set("fs.default.name", conf.get(HConstants.HBASE_DIR)); + String hbaseRootdir = conf.get(HConstants.HBASE_DIR); + + if (hbaseRootdir == null || hbaseRootdir.isEmpty()){ + throw new IllegalArgumentException("The value for '" + HConstants.HBASE_DIR + "' property can not be determined"); + } + conf.set("fs.defaultFS", hbaseRootdir); + conf.set("fs.default.name", hbaseRootdir); HBaseFsck fsck = new HBaseFsck(conf); boolean fixHoles = false; @@ -100,7 +113,17 @@ } else if (cmd.equals("-fix")) { // make all fix options true fixHoles = true; - } else { + } else if (cmd.equals("-fixVersionFile")) { + FileSystem fs = FileSystem.get(conf); + Path rootDir = new Path(hbaseRootdir); + FSUtils.setVersion(fs, rootDir); + } else if (cmd.equals("-fixClusterIdFile")) { + FileSystem fs = FileSystem.get(conf); + Path rootDir = new Path(hbaseRootdir); + FSUtils.setClusterId(fs, rootDir, UUID.randomUUID().toString(), conf.getInt( + HConstants.THREAD_WAKE_FREQUENCY, 10 * 1000)); + } + else { String str = "Unknown command line option : " + cmd; LOG.info(str); System.out.println(str); @@ -108,11 +131,8 @@ } } - // Fsck doesn't shutdown and and doesn't provide a way to shutdown its - // threads cleanly, so we do a System.exit. - boolean success = false; try { - success = fsck.rebuildMeta(fixHoles); + fsck.rebuildMeta(fixHoles); } catch (MultipleIOException mioes) { for (IOException ioe : mioes.getExceptions()) { LOG.error("Bailed out due to:", ioe); @@ -120,7 +140,17 @@ } catch (Exception e) { LOG.error("Bailed out due to: ", e); } finally { - System.exit(success ? 0 : 1); + shutDownFsck(fsck); } } + + private static void shutDownFsck(HBaseFsck fsck){ + try { + Field executorField = HBaseFsck.class.getDeclaredField("executor"); + executorField.setAccessible(true); + ((ExecutorService)executorField.get(fsck)).shutdownNow(); + } catch (Exception e) { + LOG.error("Failed to stop HBaseFsck's ExecutorService:", e); + } + } }