HBASE-9311 Create a migration script that will move data from 0.94.x to 0.96
git-svn-id: https://svn.apache.org/repos/asf/hbase/trunk@1516648 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
40f22a959b
commit
08247bfe76
|
@ -77,6 +77,7 @@ if [ $# = 0 ]; then
|
||||||
echo " hlog write-ahead-log analyzer"
|
echo " hlog write-ahead-log analyzer"
|
||||||
echo " hfile store file analyzer"
|
echo " hfile store file analyzer"
|
||||||
echo " zkcli run the ZooKeeper shell"
|
echo " zkcli run the ZooKeeper shell"
|
||||||
|
echo " upgrade upgrade hbase"
|
||||||
echo ""
|
echo ""
|
||||||
echo "PROCESS MANAGEMENT"
|
echo "PROCESS MANAGEMENT"
|
||||||
echo " master run an HBase HMaster node"
|
echo " master run an HBase HMaster node"
|
||||||
|
@ -271,6 +272,8 @@ elif [ "$COMMAND" = "hfile" ] ; then
|
||||||
CLASS='org.apache.hadoop.hbase.io.hfile.HFile'
|
CLASS='org.apache.hadoop.hbase.io.hfile.HFile'
|
||||||
elif [ "$COMMAND" = "zkcli" ] ; then
|
elif [ "$COMMAND" = "zkcli" ] ; then
|
||||||
CLASS="org.apache.hadoop.hbase.zookeeper.ZooKeeperMainServer"
|
CLASS="org.apache.hadoop.hbase.zookeeper.ZooKeeperMainServer"
|
||||||
|
elif [ "$COMMAND" = "upgrade" ] ; then
|
||||||
|
CLASS="org.apache.hadoop.hbase.migration.UpgradeTo96"
|
||||||
elif [ "$COMMAND" = "master" ] ; then
|
elif [ "$COMMAND" = "master" ] ; then
|
||||||
CLASS='org.apache.hadoop.hbase.master.HMaster'
|
CLASS='org.apache.hadoop.hbase.master.HMaster'
|
||||||
if [ "$1" != "stop" ] ; then
|
if [ "$1" != "stop" ] ; then
|
||||||
|
|
|
@ -54,7 +54,7 @@ public abstract class ReplicationStateZKBase {
|
||||||
// Public for testing
|
// Public for testing
|
||||||
public static final byte[] ENABLED_ZNODE_BYTES =
|
public static final byte[] ENABLED_ZNODE_BYTES =
|
||||||
toByteArray(ZooKeeperProtos.ReplicationState.State.ENABLED);
|
toByteArray(ZooKeeperProtos.ReplicationState.State.ENABLED);
|
||||||
protected static final byte[] DISABLED_ZNODE_BYTES =
|
public static final byte[] DISABLED_ZNODE_BYTES =
|
||||||
toByteArray(ZooKeeperProtos.ReplicationState.State.DISABLED);
|
toByteArray(ZooKeeperProtos.ReplicationState.State.DISABLED);
|
||||||
|
|
||||||
public ReplicationStateZKBase(ZooKeeperWatcher zookeeper, Configuration conf,
|
public ReplicationStateZKBase(ZooKeeperWatcher zookeeper, Configuration conf,
|
||||||
|
|
|
@ -105,6 +105,7 @@ public class NamespaceUpgrade implements Tool {
|
||||||
|
|
||||||
public void init() throws IOException {
|
public void init() throws IOException {
|
||||||
this.rootDir = FSUtils.getRootDir(conf);
|
this.rootDir = FSUtils.getRootDir(conf);
|
||||||
|
FSUtils.setFsDefault(getConf(), rootDir);
|
||||||
this.fs = FileSystem.get(conf);
|
this.fs = FileSystem.get(conf);
|
||||||
Path tmpDataDir = new Path(rootDir, TMP_DATA_DIR);
|
Path tmpDataDir = new Path(rootDir, TMP_DATA_DIR);
|
||||||
sysNsDir = new Path(tmpDataDir, NamespaceDescriptor.SYSTEM_NAMESPACE_NAME_STR);
|
sysNsDir = new Path(tmpDataDir, NamespaceDescriptor.SYSTEM_NAMESPACE_NAME_STR);
|
||||||
|
|
|
@ -0,0 +1,233 @@
|
||||||
|
/**
|
||||||
|
* 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.migration;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.commons.cli.CommandLine;
|
||||||
|
import org.apache.commons.cli.CommandLineParser;
|
||||||
|
import org.apache.commons.cli.GnuParser;
|
||||||
|
import org.apache.commons.cli.HelpFormatter;
|
||||||
|
import org.apache.commons.cli.Option;
|
||||||
|
import org.apache.commons.cli.Options;
|
||||||
|
import org.apache.commons.cli.ParseException;
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
import org.apache.hadoop.conf.Configured;
|
||||||
|
import org.apache.hadoop.hbase.Abortable;
|
||||||
|
import org.apache.hadoop.hbase.HBaseConfiguration;
|
||||||
|
import org.apache.hadoop.hbase.HConstants;
|
||||||
|
import org.apache.hadoop.hbase.util.Bytes;
|
||||||
|
import org.apache.hadoop.hbase.util.HFileV1Detector;
|
||||||
|
import org.apache.hadoop.hbase.util.ZKDataMigrator;
|
||||||
|
import org.apache.hadoop.hbase.zookeeper.ZKUtil;
|
||||||
|
import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher;
|
||||||
|
import org.apache.hadoop.util.Tool;
|
||||||
|
import org.apache.hadoop.util.ToolRunner;
|
||||||
|
|
||||||
|
public class UpgradeTo96 extends Configured implements Tool {
|
||||||
|
private static final Log LOG = LogFactory.getLog(UpgradeTo96.class);
|
||||||
|
|
||||||
|
private Options options = new Options();
|
||||||
|
/**
|
||||||
|
* whether to do overall upgrade (namespace and znodes)
|
||||||
|
*/
|
||||||
|
private boolean upgrade;
|
||||||
|
/**
|
||||||
|
* whether to check for HFileV1
|
||||||
|
*/
|
||||||
|
private boolean checkForHFileV1;
|
||||||
|
/**
|
||||||
|
* Path of directory to check for HFileV1
|
||||||
|
*/
|
||||||
|
private String dirToCheckForHFileV1;
|
||||||
|
|
||||||
|
UpgradeTo96() {
|
||||||
|
setOptions();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setOptions() {
|
||||||
|
options.addOption("h", "help", false, "Help");
|
||||||
|
options.addOption(new Option("check", false, "Run upgrade check; looks for HFileV1 "
|
||||||
|
+ " under ${hbase.rootdir} or provided 'dir' directory."));
|
||||||
|
options.addOption(new Option("execute", false, "Run upgrade; zk and hdfs must be up, hbase down"));
|
||||||
|
Option pathOption = new Option("dir", true,
|
||||||
|
"Relative path of dir to check for HFileV1s.");
|
||||||
|
pathOption.setRequired(false);
|
||||||
|
options.addOption(pathOption);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean parseOption(String[] args) throws ParseException {
|
||||||
|
if (args.length == 0) return false; // no args shows help.
|
||||||
|
|
||||||
|
CommandLineParser parser = new GnuParser();
|
||||||
|
CommandLine cmd = parser.parse(options, args);
|
||||||
|
if (cmd.hasOption("h")) {
|
||||||
|
printUsage();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (cmd.hasOption("execute")) upgrade = true;
|
||||||
|
if (cmd.hasOption("check")) checkForHFileV1 = true;
|
||||||
|
if (checkForHFileV1 && cmd.hasOption("dir")) {
|
||||||
|
this.dirToCheckForHFileV1 = cmd.getOptionValue("dir");
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void printUsage() {
|
||||||
|
HelpFormatter formatter = new HelpFormatter();
|
||||||
|
formatter.printHelp("$bin/hbase upgrade -check [-dir DIR]|-execute", options);
|
||||||
|
System.out.println("Read http://hbase.apache.org/book.html#upgrade0.96 before attempting upgrade");
|
||||||
|
System.out.println();
|
||||||
|
System.out.println("Example usage:");
|
||||||
|
System.out.println();
|
||||||
|
System.out.println("Run upgrade check; looks for HFileV1s under ${hbase.rootdir}:");
|
||||||
|
System.out.println(" $ bin/hbase upgrade -check");
|
||||||
|
System.out.println();
|
||||||
|
System.out.println("Run the upgrade: ");
|
||||||
|
System.out.println(" $ bin/hbase upgrade -execute");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int run(String[] args) throws Exception {
|
||||||
|
if (!parseOption(args)) {
|
||||||
|
printUsage();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (checkForHFileV1) {
|
||||||
|
int res = doHFileV1Check();
|
||||||
|
if (res == 0) LOG.info("No HFileV1 found.");
|
||||||
|
else {
|
||||||
|
LOG.warn("There are some HFileV1, or corrupt files (files with incorrect major version).");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// if the user wants to upgrade, check for any HBase live process.
|
||||||
|
// If yes, prompt the user to stop them
|
||||||
|
if (upgrade) {
|
||||||
|
if (isAnyHBaseProcessAlive()) {
|
||||||
|
LOG.error("Some HBase processes are still alive, or znodes not expired yet. "
|
||||||
|
+ "Please stop them before upgrade or try after some time.");
|
||||||
|
throw new IOException("Some HBase processes are still alive, or znodes not expired yet");
|
||||||
|
}
|
||||||
|
return upgradeNamespaceAndZnodes();
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isAnyHBaseProcessAlive() throws IOException {
|
||||||
|
ZooKeeperWatcher zkw = null;
|
||||||
|
try {
|
||||||
|
zkw = new ZooKeeperWatcher(getConf(), "Check Live Processes.", new Abortable() {
|
||||||
|
private boolean aborted = false;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void abort(String why, Throwable e) {
|
||||||
|
LOG.warn("Got aborted with reason: " + why + ", and error: " + e);
|
||||||
|
this.aborted = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAborted() {
|
||||||
|
return this.aborted;
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
boolean liveProcessesExists = false;
|
||||||
|
if (ZKUtil.checkExists(zkw, zkw.baseZNode) == -1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (ZKUtil.checkExists(zkw, zkw.backupMasterAddressesZNode) != -1) {
|
||||||
|
List<String> backupMasters = ZKUtil
|
||||||
|
.listChildrenNoWatch(zkw, zkw.backupMasterAddressesZNode);
|
||||||
|
if (!backupMasters.isEmpty()) {
|
||||||
|
LOG.warn("Backup master(s) " + backupMasters
|
||||||
|
+ " are alive or backup-master znodes not expired.");
|
||||||
|
liveProcessesExists = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ZKUtil.checkExists(zkw, zkw.rsZNode) != -1) {
|
||||||
|
List<String> regionServers = ZKUtil.listChildrenNoWatch(zkw, zkw.rsZNode);
|
||||||
|
if (!regionServers.isEmpty()) {
|
||||||
|
LOG.warn("Region server(s) " + regionServers + " are alive or rs znodes not expired.");
|
||||||
|
liveProcessesExists = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ZKUtil.checkExists(zkw, zkw.getMasterAddressZNode()) != -1) {
|
||||||
|
byte[] data = ZKUtil.getData(zkw, zkw.getMasterAddressZNode());
|
||||||
|
if (data != null && !Bytes.equals(data, HConstants.EMPTY_BYTE_ARRAY)) {
|
||||||
|
LOG.warn("Active master at address " + Bytes.toString(data)
|
||||||
|
+ " is still alive or master znode not expired.");
|
||||||
|
liveProcessesExists = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return liveProcessesExists;
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOG.error("Got exception while checking live hbase processes", e);
|
||||||
|
throw new IOException(e);
|
||||||
|
} finally {
|
||||||
|
if (zkw != null) {
|
||||||
|
zkw.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int doHFileV1Check() throws Exception {
|
||||||
|
String[] args = null;
|
||||||
|
if (dirToCheckForHFileV1 != null) args = new String[] { "-p" + dirToCheckForHFileV1 };
|
||||||
|
return ToolRunner.run(getConf(), new HFileV1Detector(), args);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int upgradeNamespaceAndZnodes() throws Exception {
|
||||||
|
int res = upgradeNamespace();
|
||||||
|
if (res == 0) return upgradeZnodes();//upgrade znodes only if we succeed in first step.
|
||||||
|
else {
|
||||||
|
LOG.warn("Namespace upgrade returned: "+res +", expected 0. Aborting the upgrade");
|
||||||
|
throw new Exception("Unexpected return code from Namespace upgrade");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int upgradeNamespace() throws Exception {
|
||||||
|
LOG.info("Upgrading Namespace");
|
||||||
|
try {
|
||||||
|
int res = ToolRunner.run(getConf(), new NamespaceUpgrade(), new String[] { "--upgrade" });
|
||||||
|
LOG.info("Successfully Upgraded NameSpace.");
|
||||||
|
return res;
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOG.error("Got exception while upgrading Namespace", e);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int upgradeZnodes() throws Exception {
|
||||||
|
LOG.info("Upgrading Znodes");
|
||||||
|
try {
|
||||||
|
int res = ToolRunner.run(getConf(), new ZKDataMigrator(), null);
|
||||||
|
LOG.info("Succesfully upgraded znodes.");
|
||||||
|
return res;
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOG.error("Got exception while upgrading Znodes", e);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
System.exit(ToolRunner.run(HBaseConfiguration.create(), new UpgradeTo96(), args));
|
||||||
|
}
|
||||||
|
}
|
|
@ -46,7 +46,6 @@ import org.apache.hadoop.fs.FileSystem;
|
||||||
import org.apache.hadoop.fs.Path;
|
import org.apache.hadoop.fs.Path;
|
||||||
import org.apache.hadoop.hbase.HBaseConfiguration;
|
import org.apache.hadoop.hbase.HBaseConfiguration;
|
||||||
import org.apache.hadoop.hbase.io.HFileLink;
|
import org.apache.hadoop.hbase.io.HFileLink;
|
||||||
import org.apache.hadoop.hbase.io.hfile.FixedFileTrailer;
|
|
||||||
import org.apache.hadoop.hbase.regionserver.HRegionFileSystem;
|
import org.apache.hadoop.hbase.regionserver.HRegionFileSystem;
|
||||||
import org.apache.hadoop.hbase.regionserver.StoreFileInfo;
|
import org.apache.hadoop.hbase.regionserver.StoreFileInfo;
|
||||||
import org.apache.hadoop.util.Tool;
|
import org.apache.hadoop.util.Tool;
|
||||||
|
@ -69,9 +68,27 @@ public class HFileV1Detector extends Configured implements Tool {
|
||||||
private static final Log LOG = LogFactory.getLog(HFileV1Detector.class);
|
private static final Log LOG = LogFactory.getLog(HFileV1Detector.class);
|
||||||
private static final int DEFAULT_NUM_OF_THREADS = 10;
|
private static final int DEFAULT_NUM_OF_THREADS = 10;
|
||||||
private int numOfThreads;
|
private int numOfThreads;
|
||||||
private Path dirToProcess;
|
/**
|
||||||
|
* directory to start the processing.
|
||||||
|
*/
|
||||||
|
private Path targetDirPath;
|
||||||
|
/**
|
||||||
|
* executor for processing regions.
|
||||||
|
*/
|
||||||
|
private ExecutorService exec;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Keeps record of processed tables.
|
||||||
|
*/
|
||||||
|
private final Set<Path> processedTables = new HashSet<Path>();
|
||||||
|
/**
|
||||||
|
* set of corrupted HFiles (with undetermined major version)
|
||||||
|
*/
|
||||||
private final Set<Path> corruptedHFiles = Collections
|
private final Set<Path> corruptedHFiles = Collections
|
||||||
.newSetFromMap(new ConcurrentHashMap<Path, Boolean>());
|
.newSetFromMap(new ConcurrentHashMap<Path, Boolean>());
|
||||||
|
/**
|
||||||
|
* set of HfileV1;
|
||||||
|
*/
|
||||||
private final Set<Path> hFileV1Set = Collections
|
private final Set<Path> hFileV1Set = Collections
|
||||||
.newSetFromMap(new ConcurrentHashMap<Path, Boolean>());
|
.newSetFromMap(new ConcurrentHashMap<Path, Boolean>());
|
||||||
|
|
||||||
|
@ -107,75 +124,101 @@ public class HFileV1Detector extends Configured implements Tool {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cmd.hasOption("p")) {
|
if (cmd.hasOption("p")) {
|
||||||
dirToProcess = new Path(cmd.getOptionValue("p"));
|
this.targetDirPath = new Path(FSUtils.getRootDir(getConf()), cmd.getOptionValue("p"));
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
if (cmd.hasOption("n")) {
|
if (cmd.hasOption("n")) {
|
||||||
int n = Integer.parseInt(cmd.getOptionValue("n"));
|
int n = Integer.parseInt(cmd.getOptionValue("n"));
|
||||||
if (n < 0 || n > 100) {
|
if (n < 0 || n > 100) {
|
||||||
System.out.println("Please use a positive number <= 100 for number of threads."
|
LOG.warn("Please use a positive number <= 100 for number of threads."
|
||||||
+ " Continuing with default value " + DEFAULT_NUM_OF_THREADS);
|
+ " Continuing with default value " + DEFAULT_NUM_OF_THREADS);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
numOfThreads = n;
|
this.numOfThreads = n;
|
||||||
}
|
}
|
||||||
} catch (NumberFormatException nfe) {
|
} catch (NumberFormatException nfe) {
|
||||||
System.err.println("Please select a valid number for threads");
|
LOG.error("Please select a valid number for threads");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks for HFileV1.
|
||||||
|
* @return 0 when no HFileV1 is present.
|
||||||
|
* 1 when a HFileV1 is present or, when there is a file with corrupt major version
|
||||||
|
* (neither V1 nor V2).
|
||||||
|
* -1 in case of any error/exception
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public int run(String args[]) throws IOException, ParseException {
|
public int run(String args[]) throws IOException, ParseException {
|
||||||
|
FSUtils.setFsDefault(getConf(), new Path(FSUtils.getRootDir(getConf()).toUri()));
|
||||||
fs = FileSystem.get(getConf());
|
fs = FileSystem.get(getConf());
|
||||||
numOfThreads = DEFAULT_NUM_OF_THREADS;
|
numOfThreads = DEFAULT_NUM_OF_THREADS;
|
||||||
dirToProcess = FSUtils.getRootDir(getConf());
|
targetDirPath = FSUtils.getRootDir(getConf());
|
||||||
if (!parseOption(args)) {
|
if (!parseOption(args)) {
|
||||||
System.exit(1);
|
System.exit(-1);
|
||||||
}
|
}
|
||||||
ExecutorService exec = Executors.newFixedThreadPool(numOfThreads);
|
this.exec = Executors.newFixedThreadPool(numOfThreads);
|
||||||
Set<Path> regionsWithHFileV1;
|
|
||||||
try {
|
try {
|
||||||
regionsWithHFileV1 = checkForV1Files(dirToProcess, exec);
|
return processResult(checkForV1Files(targetDirPath));
|
||||||
printHRegionsWithHFileV1(regionsWithHFileV1);
|
|
||||||
printAllHFileV1();
|
|
||||||
printCorruptedHFiles();
|
|
||||||
if (hFileV1Set.isEmpty() && corruptedHFiles.isEmpty()) {
|
|
||||||
// all clear.
|
|
||||||
System.out.println("No HFile V1 Found");
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
System.err.println(e);
|
LOG.error(e);
|
||||||
return 1;
|
|
||||||
} finally {
|
} finally {
|
||||||
exec.shutdown();
|
exec.shutdown();
|
||||||
fs.close();
|
fs.close();
|
||||||
}
|
}
|
||||||
return 0;
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int processResult(Set<Path> regionsWithHFileV1) {
|
||||||
|
LOG.info("Result: \n");
|
||||||
|
printSet(processedTables, "Tables Processed: ");
|
||||||
|
|
||||||
|
int count = hFileV1Set.size();
|
||||||
|
LOG.info("Count of HFileV1: " + count);
|
||||||
|
if (count > 0) printSet(hFileV1Set, "HFileV1:");
|
||||||
|
|
||||||
|
count = corruptedHFiles.size();
|
||||||
|
LOG.info("Count of corrupted files: " + count);
|
||||||
|
if (count > 0) printSet(corruptedHFiles, "Corrupted Files: ");
|
||||||
|
|
||||||
|
count = regionsWithHFileV1.size();
|
||||||
|
LOG.info("Count of Regions with HFileV1: " + count);
|
||||||
|
if (count > 0) printSet(regionsWithHFileV1, "Regions to Major Compact: ");
|
||||||
|
|
||||||
|
return (hFileV1Set.isEmpty() && corruptedHFiles.isEmpty()) ? 0 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void printSet(Set<Path> result, String msg) {
|
||||||
|
LOG.info(msg);
|
||||||
|
for (Path p : result) {
|
||||||
|
LOG.info(p);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Takes a directory path, and lists out any HFileV1, if present.
|
* Takes a directory path, and lists out any HFileV1, if present.
|
||||||
* @param targetDir directory to start looking for HFilev1.
|
* @param targetDir directory to start looking for HFilev1.
|
||||||
* @param exec
|
|
||||||
* @return set of Regions that have HFileV1
|
* @return set of Regions that have HFileV1
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
*/
|
*/
|
||||||
private Set<Path> checkForV1Files(Path targetDir, final ExecutorService exec) throws IOException {
|
private Set<Path> checkForV1Files(Path targetDir) throws IOException {
|
||||||
if (isTableDir(fs, targetDir)) {
|
LOG.info("Target dir is: " + targetDir);
|
||||||
return processTable(targetDir, exec);
|
|
||||||
}
|
|
||||||
// user has passed a hbase installation directory.
|
|
||||||
if (!fs.exists(targetDir)) {
|
if (!fs.exists(targetDir)) {
|
||||||
throw new IOException("The given path does not exist: " + targetDir);
|
throw new IOException("The given path does not exist: " + targetDir);
|
||||||
}
|
}
|
||||||
|
if (isTableDir(fs, targetDir)) {
|
||||||
|
processedTables.add(targetDir);
|
||||||
|
return processTable(targetDir);
|
||||||
|
}
|
||||||
Set<Path> regionsWithHFileV1 = new HashSet<Path>();
|
Set<Path> regionsWithHFileV1 = new HashSet<Path>();
|
||||||
FileStatus[] fsStats = fs.listStatus(targetDir);
|
FileStatus[] fsStats = fs.listStatus(targetDir);
|
||||||
for (FileStatus fsStat : fsStats) {
|
for (FileStatus fsStat : fsStats) {
|
||||||
if (isTableDir(fs, fsStat.getPath())) {
|
if (isTableDir(fs, fsStat.getPath()) && !isRootTable(fsStat.getPath())) {
|
||||||
|
processedTables.add(fsStat.getPath());
|
||||||
// look for regions and find out any v1 file.
|
// look for regions and find out any v1 file.
|
||||||
regionsWithHFileV1.addAll(processTable(fsStat.getPath(), exec));
|
regionsWithHFileV1.addAll(processTable(fsStat.getPath()));
|
||||||
} else {
|
} else {
|
||||||
LOG.info("Ignoring path: " + fsStat.getPath());
|
LOG.info("Ignoring path: " + fsStat.getPath());
|
||||||
}
|
}
|
||||||
|
@ -184,15 +227,24 @@ public class HFileV1Detector extends Configured implements Tool {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find out the regions in the table which has an HFile v1 in it.
|
* Ignore ROOT table as it doesn't exist in 0.96.
|
||||||
|
* @param path
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private boolean isRootTable(Path path) {
|
||||||
|
if (path != null && path.toString().endsWith("-ROOT-")) return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find out regions in the table which have HFileV1.
|
||||||
* @param tableDir
|
* @param tableDir
|
||||||
* @param exec
|
|
||||||
* @return the set of regions containing HFile v1.
|
* @return the set of regions containing HFile v1.
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
*/
|
*/
|
||||||
private Set<Path> processTable(Path tableDir, final ExecutorService exec) throws IOException {
|
private Set<Path> processTable(Path tableDir) throws IOException {
|
||||||
// list out the regions and then process each file in it.
|
// list out the regions and then process each file in it.
|
||||||
LOG.info("processing table: " + tableDir);
|
LOG.debug("processing table: " + tableDir);
|
||||||
List<Future<Path>> regionLevelResults = new ArrayList<Future<Path>>();
|
List<Future<Path>> regionLevelResults = new ArrayList<Future<Path>>();
|
||||||
Set<Path> regionsWithHFileV1 = new HashSet<Path>();
|
Set<Path> regionsWithHFileV1 = new HashSet<Path>();
|
||||||
|
|
||||||
|
@ -200,7 +252,7 @@ public class HFileV1Detector extends Configured implements Tool {
|
||||||
for (FileStatus fsStat : fsStats) {
|
for (FileStatus fsStat : fsStats) {
|
||||||
// process each region
|
// process each region
|
||||||
if (isRegionDir(fs, fsStat.getPath())) {
|
if (isRegionDir(fs, fsStat.getPath())) {
|
||||||
regionLevelResults.add(processRegion(fsStat.getPath(), exec));
|
regionLevelResults.add(processRegion(fsStat.getPath()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (Future<Path> f : regionLevelResults) {
|
for (Future<Path> f : regionLevelResults) {
|
||||||
|
@ -209,9 +261,9 @@ public class HFileV1Detector extends Configured implements Tool {
|
||||||
regionsWithHFileV1.add(f.get());
|
regionsWithHFileV1.add(f.get());
|
||||||
}
|
}
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
System.err.println(e);
|
LOG.error(e);
|
||||||
} catch (ExecutionException e) {
|
} catch (ExecutionException e) {
|
||||||
System.err.println(e); // might be a bad hfile. We print it at the end.
|
LOG.error(e); // might be a bad hfile. We print it at the end.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return regionsWithHFileV1;
|
return regionsWithHFileV1;
|
||||||
|
@ -221,11 +273,10 @@ public class HFileV1Detector extends Configured implements Tool {
|
||||||
* Each region is processed by a separate handler. If a HRegion has a hfileV1, its path is
|
* Each region is processed by a separate handler. If a HRegion has a hfileV1, its path is
|
||||||
* returned as the future result, otherwise, a null value is returned.
|
* returned as the future result, otherwise, a null value is returned.
|
||||||
* @param regionDir Region to process.
|
* @param regionDir Region to process.
|
||||||
* @param exec
|
|
||||||
* @return corresponding Future object.
|
* @return corresponding Future object.
|
||||||
*/
|
*/
|
||||||
private Future<Path> processRegion(final Path regionDir, final ExecutorService exec) {
|
private Future<Path> processRegion(final Path regionDir) {
|
||||||
LOG.info("processing region: " + regionDir);
|
LOG.debug("processing region: " + regionDir);
|
||||||
Callable<Path> regionCallable = new Callable<Path>() {
|
Callable<Path> regionCallable = new Callable<Path>() {
|
||||||
@Override
|
@Override
|
||||||
public Path call() throws Exception {
|
public Path call() throws Exception {
|
||||||
|
@ -249,15 +300,17 @@ public class HFileV1Detector extends Configured implements Tool {
|
||||||
fsdis = fs.open(storeFilePath);
|
fsdis = fs.open(storeFilePath);
|
||||||
lenToRead = storeFile.getLen();
|
lenToRead = storeFile.getLen();
|
||||||
}
|
}
|
||||||
FixedFileTrailer trailer = FixedFileTrailer.readFromStream(fsdis, lenToRead);
|
int majorVersion = computeMajorVersion(fsdis, lenToRead);
|
||||||
int version = trailer.getMajorVersion();
|
if (majorVersion == 1) {
|
||||||
if (version == 1) {
|
|
||||||
hFileV1Set.add(storeFilePath);
|
hFileV1Set.add(storeFilePath);
|
||||||
// return this region path, as it needs to be compacted.
|
// return this region path, as it needs to be compacted.
|
||||||
return regionDir;
|
return regionDir;
|
||||||
}
|
}
|
||||||
|
if (majorVersion > 2 || majorVersion < 1) throw new IllegalArgumentException(
|
||||||
|
"Incorrect major version: " + majorVersion);
|
||||||
} catch (Exception iae) {
|
} catch (Exception iae) {
|
||||||
corruptedHFiles.add(storeFilePath);
|
corruptedHFiles.add(storeFilePath);
|
||||||
|
LOG.error("Got exception while reading trailer for file: "+ storeFilePath, iae);
|
||||||
} finally {
|
} finally {
|
||||||
if (fsdis != null) fsdis.close();
|
if (fsdis != null) fsdis.close();
|
||||||
}
|
}
|
||||||
|
@ -265,13 +318,30 @@ public class HFileV1Detector extends Configured implements Tool {
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private int computeMajorVersion(FSDataInputStream istream, long fileSize)
|
||||||
|
throws IOException {
|
||||||
|
//read up the last int of the file. Major version is in the last 3 bytes.
|
||||||
|
long seekPoint = fileSize - Bytes.SIZEOF_INT;
|
||||||
|
if (seekPoint < 0)
|
||||||
|
throw new IllegalArgumentException("File too small, no major version found");
|
||||||
|
|
||||||
|
// Read the version from the last int of the file.
|
||||||
|
istream.seek(seekPoint);
|
||||||
|
int version = istream.readInt();
|
||||||
|
// Extract and return the major version
|
||||||
|
return version & 0x00ffffff;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
Future<Path> f = exec.submit(regionCallable);
|
Future<Path> f = exec.submit(regionCallable);
|
||||||
return f;
|
return f;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isTableDir(final FileSystem fs, final Path path) throws IOException {
|
private static boolean isTableDir(final FileSystem fs, final Path path) throws IOException {
|
||||||
return FSTableDescriptors.getTableInfoPath(fs, path) != null;
|
// check for old format, of having /table/.tableinfo; .META. doesn't has .tableinfo,
|
||||||
|
// include it.
|
||||||
|
return (FSTableDescriptors.getTableInfoPath(fs, path) != null || FSTableDescriptors
|
||||||
|
.getCurrentTableInfoStatus(fs, path, false) != null) || path.toString().endsWith(".META.");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isRegionDir(final FileSystem fs, final Path path) throws IOException {
|
private static boolean isRegionDir(final FileSystem fs, final Path path) throws IOException {
|
||||||
|
@ -280,43 +350,6 @@ public class HFileV1Detector extends Configured implements Tool {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void printHRegionsWithHFileV1(Set<Path> regionsHavingHFileV1) {
|
|
||||||
if (!regionsHavingHFileV1.isEmpty()) {
|
|
||||||
System.out.println();
|
|
||||||
System.out.println("Following regions has HFileV1 and needs to be Major Compacted:");
|
|
||||||
System.out.println();
|
|
||||||
for (Path r : regionsHavingHFileV1) {
|
|
||||||
System.out.println(r);
|
|
||||||
}
|
|
||||||
System.out.println();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void printAllHFileV1() {
|
|
||||||
if (!hFileV1Set.isEmpty()) {
|
|
||||||
System.out.println();
|
|
||||||
System.out.println("Following HFileV1 are found:");
|
|
||||||
System.out.println();
|
|
||||||
for (Path r : hFileV1Set) {
|
|
||||||
System.out.println(r);
|
|
||||||
}
|
|
||||||
System.out.println();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private void printCorruptedHFiles() {
|
|
||||||
if (!corruptedHFiles.isEmpty()) {
|
|
||||||
System.out.println();
|
|
||||||
System.out.println("Following HFiles are corrupted as their version is unknown:");
|
|
||||||
System.out.println();
|
|
||||||
for (Path r : corruptedHFiles) {
|
|
||||||
System.out.println(r);
|
|
||||||
}
|
|
||||||
System.out.println();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void main(String args[]) throws Exception {
|
public static void main(String args[]) throws Exception {
|
||||||
System.exit(ToolRunner.run(HBaseConfiguration.create(), new HFileV1Detector(), args));
|
System.exit(ToolRunner.run(HBaseConfiguration.create(), new HFileV1Detector(), args));
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,261 @@
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
import org.apache.hadoop.conf.Configuration;
|
||||||
|
import org.apache.hadoop.conf.Configured;
|
||||||
|
import org.apache.hadoop.hbase.Abortable;
|
||||||
|
import org.apache.hadoop.hbase.HBaseConfiguration;
|
||||||
|
import org.apache.hadoop.hbase.HConstants;
|
||||||
|
import org.apache.hadoop.hbase.master.snapshot.SnapshotManager;
|
||||||
|
import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
|
||||||
|
import org.apache.hadoop.hbase.protobuf.generated.ZooKeeperProtos;
|
||||||
|
import org.apache.hadoop.hbase.protobuf.generated.ZooKeeperProtos.ReplicationPeer;
|
||||||
|
import org.apache.hadoop.hbase.replication.ReplicationStateZKBase;
|
||||||
|
import org.apache.hadoop.hbase.zookeeper.ZKUtil;
|
||||||
|
import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher;
|
||||||
|
import org.apache.hadoop.util.Tool;
|
||||||
|
import org.apache.hadoop.util.ToolRunner;
|
||||||
|
import org.apache.zookeeper.KeeperException;
|
||||||
|
import org.apache.zookeeper.KeeperException.NoNodeException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tool to migrate zookeeper data of older hbase versions(<0.95.0) to PB.
|
||||||
|
*/
|
||||||
|
public class ZKDataMigrator extends Configured implements Tool {
|
||||||
|
|
||||||
|
private static final Log LOG = LogFactory.getLog(ZKDataMigrator.class);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int run(String[] as) throws Exception {
|
||||||
|
Configuration conf = getConf();
|
||||||
|
ZooKeeperWatcher zkw = null;
|
||||||
|
try {
|
||||||
|
zkw = new ZooKeeperWatcher(getConf(), "Migrate ZK data to PB.",
|
||||||
|
new ZKDataMigratorAbortable());
|
||||||
|
if (ZKUtil.checkExists(zkw, zkw.baseZNode) == -1) {
|
||||||
|
LOG.info("No hbase related data available in zookeeper. returning..");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
List<String> children = ZKUtil.listChildrenNoWatch(zkw, zkw.baseZNode);
|
||||||
|
if (children == null) {
|
||||||
|
LOG.info("No child nodes to mirgrate. returning..");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
String childPath = null;
|
||||||
|
for (String child : children) {
|
||||||
|
childPath = ZKUtil.joinZNode(zkw.baseZNode, child);
|
||||||
|
if (child.equals(conf.get("zookeeper.znode.rootserver", "root-region-server"))) {
|
||||||
|
// -ROOT- region no longer present from 0.95.0, so we can remove this
|
||||||
|
// znode
|
||||||
|
ZKUtil.deleteNodeRecursively(zkw, childPath);
|
||||||
|
// TODO delete root table path from file system.
|
||||||
|
} else if (child.equals(conf.get("zookeeper.znode.rs", "rs"))) {
|
||||||
|
// Since there is no live region server instance during migration, we
|
||||||
|
// can remove this znode as well.
|
||||||
|
ZKUtil.deleteNodeRecursively(zkw, childPath);
|
||||||
|
} else if (child.equals(conf.get("zookeeper.znode.draining.rs", "draining"))) {
|
||||||
|
// If we want to migrate to 0.95.0 from older versions we need to stop
|
||||||
|
// the existing cluster. So there wont be any draining servers so we
|
||||||
|
// can
|
||||||
|
// remove it.
|
||||||
|
ZKUtil.deleteNodeRecursively(zkw, childPath);
|
||||||
|
} else if (child.equals(conf.get("zookeeper.znode.master", "master"))) {
|
||||||
|
// Since there is no live master instance during migration, we can
|
||||||
|
// remove this znode as well.
|
||||||
|
ZKUtil.deleteNodeRecursively(zkw, childPath);
|
||||||
|
} else if (child.equals(conf.get("zookeeper.znode.backup.masters", "backup-masters"))) {
|
||||||
|
// Since there is no live backup master instances during migration, we
|
||||||
|
// can remove this znode as well.
|
||||||
|
ZKUtil.deleteNodeRecursively(zkw, childPath);
|
||||||
|
} else if (child.equals(conf.get("zookeeper.znode.state", "shutdown"))) {
|
||||||
|
// shutdown node is not present from 0.95.0 onwards. Its renamed to
|
||||||
|
// "running". We can delete it.
|
||||||
|
ZKUtil.deleteNodeRecursively(zkw, childPath);
|
||||||
|
} else if (child.equals(conf.get("zookeeper.znode.unassigned", "unassigned"))) {
|
||||||
|
// Any way during clean cluster startup we will remove all unassigned
|
||||||
|
// region nodes. we can delete all children nodes as well. This znode
|
||||||
|
// is
|
||||||
|
// renamed to "regions-in-transition" from 0.95.0 onwards.
|
||||||
|
ZKUtil.deleteNodeRecursively(zkw, childPath);
|
||||||
|
} else if (child.equals(conf.get("zookeeper.znode.tableEnableDisable", "table"))
|
||||||
|
|| child.equals(conf.get("zookeeper.znode.masterTableEnableDisable", "table"))) {
|
||||||
|
checkAndMigrateTableStatesToPB(zkw);
|
||||||
|
} else if (child.equals(conf.get("zookeeper.znode.masterTableEnableDisable92",
|
||||||
|
"table92"))) {
|
||||||
|
// This is replica of table states from tableZnode so we can remove
|
||||||
|
// this.
|
||||||
|
ZKUtil.deleteNodeRecursively(zkw, childPath);
|
||||||
|
} else if (child.equals(conf.get("zookeeper.znode.splitlog", "splitlog"))) {
|
||||||
|
// This znode no longer available from 0.95.0 onwards, we can remove
|
||||||
|
// it.
|
||||||
|
ZKUtil.deleteNodeRecursively(zkw, childPath);
|
||||||
|
} else if (child.equals(conf.get("zookeeper.znode.replication", "replication"))) {
|
||||||
|
checkAndMigrateReplicationNodesToPB(zkw);
|
||||||
|
} else if (child.equals(conf.get("zookeeper.znode.clusterId", "hbaseid"))) {
|
||||||
|
// it will be re-created by master.
|
||||||
|
ZKUtil.deleteNodeRecursively(zkw, childPath);
|
||||||
|
} else if (child.equals(SnapshotManager.ONLINE_SNAPSHOT_CONTROLLER_DESCRIPTION)) {
|
||||||
|
// not needed as it is transient.
|
||||||
|
ZKUtil.deleteNodeRecursively(zkw, childPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOG.error("Got exception while updating znodes ", e);
|
||||||
|
throw new IOException(e);
|
||||||
|
} finally {
|
||||||
|
if (zkw != null) {
|
||||||
|
zkw.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkAndMigrateTableStatesToPB(ZooKeeperWatcher zkw) throws KeeperException {
|
||||||
|
List<String> tables = ZKUtil.listChildrenNoWatch(zkw, zkw.tableZNode);
|
||||||
|
if (tables == null) {
|
||||||
|
LOG.info("No table present to migrate table state to PB. returning..");
|
||||||
|
}
|
||||||
|
for (String table : tables) {
|
||||||
|
String znode = ZKUtil.joinZNode(zkw.tableZNode, table);
|
||||||
|
// Delete -ROOT- table state znode since its no longer present in 0.95.0
|
||||||
|
// onwards.
|
||||||
|
if (table.equals("-ROOT-") || table.equals(".META.")) {
|
||||||
|
ZKUtil.deleteNode(zkw, znode);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
byte[] data = ZKUtil.getData(zkw, znode);
|
||||||
|
if (ProtobufUtil.isPBMagicPrefix(data)) continue;
|
||||||
|
ZooKeeperProtos.Table.Builder builder = ZooKeeperProtos.Table.newBuilder();
|
||||||
|
builder.setState(ZooKeeperProtos.Table.State.valueOf(Bytes.toString(data)));
|
||||||
|
data = ProtobufUtil.prependPBMagic(builder.build().toByteArray());
|
||||||
|
ZKUtil.setData(zkw, znode, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkAndMigrateReplicationNodesToPB(ZooKeeperWatcher zkw) throws KeeperException {
|
||||||
|
String replicationZnodeName = getConf().get("zookeeper.znode.replication", "replication");
|
||||||
|
String replicationPath = ZKUtil.joinZNode(zkw.baseZNode, replicationZnodeName);
|
||||||
|
List<String> replicationZnodes = ZKUtil.listChildrenNoWatch(zkw, replicationPath);
|
||||||
|
if (replicationZnodes == null) {
|
||||||
|
LOG.info("No replication related znodes present to migrate. returning..");
|
||||||
|
}
|
||||||
|
for (String child : replicationZnodes) {
|
||||||
|
String znode = ZKUtil.joinZNode(replicationPath, child);
|
||||||
|
if (child.equals(getConf().get("zookeeper.znode.replication.peers", "peers"))) {
|
||||||
|
List<String> peers = ZKUtil.listChildrenNoWatch(zkw, znode);
|
||||||
|
if (peers == null || peers.isEmpty()) {
|
||||||
|
LOG.info("No peers present to migrate. returning..");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
checkAndMigratePeerZnodesToPB(zkw, znode, peers);
|
||||||
|
} else if (child.equals(getConf().get("zookeeper.znode.replication.state", "state"))) {
|
||||||
|
// This is no longer used in >=0.95.x
|
||||||
|
ZKUtil.deleteNodeRecursively(zkw, znode);
|
||||||
|
} else if (child.equals(getConf().get("zookeeper.znode.replication.rs", "rs"))) {
|
||||||
|
List<String> rsList = ZKUtil.listChildrenNoWatch(zkw, znode);
|
||||||
|
if (rsList == null || rsList.isEmpty()) continue;
|
||||||
|
for (String rs : rsList) {
|
||||||
|
checkAndMigrateQueuesToPB(zkw, znode, rs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkAndMigrateQueuesToPB(ZooKeeperWatcher zkw, String znode, String rs)
|
||||||
|
throws KeeperException, NoNodeException {
|
||||||
|
String rsPath = ZKUtil.joinZNode(znode, rs);
|
||||||
|
List<String> peers = ZKUtil.listChildrenNoWatch(zkw, rsPath);
|
||||||
|
if (peers == null || peers.isEmpty()) return;
|
||||||
|
String peerPath = null;
|
||||||
|
for (String peer : peers) {
|
||||||
|
peerPath = ZKUtil.joinZNode(rsPath, peer);
|
||||||
|
List<String> files = ZKUtil.listChildrenNoWatch(zkw, peerPath);
|
||||||
|
if (files == null || files.isEmpty()) continue;
|
||||||
|
String filePath = null;
|
||||||
|
for (String file : files) {
|
||||||
|
filePath = ZKUtil.joinZNode(peerPath, file);
|
||||||
|
byte[] data = ZKUtil.getData(zkw, filePath);
|
||||||
|
if (data == null || Bytes.equals(data, HConstants.EMPTY_BYTE_ARRAY)) continue;
|
||||||
|
if (ProtobufUtil.isPBMagicPrefix(data)) continue;
|
||||||
|
ZKUtil.setData(zkw, filePath,
|
||||||
|
ZKUtil.positionToByteArray(Long.parseLong(Bytes.toString(data))));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkAndMigratePeerZnodesToPB(ZooKeeperWatcher zkw, String znode,
|
||||||
|
List<String> peers) throws KeeperException, NoNodeException {
|
||||||
|
for (String peer : peers) {
|
||||||
|
String peerZnode = ZKUtil.joinZNode(znode, peer);
|
||||||
|
byte[] data = ZKUtil.getData(zkw, peerZnode);
|
||||||
|
if (!ProtobufUtil.isPBMagicPrefix(data)) {
|
||||||
|
migrateClusterKeyToPB(zkw, peerZnode, data);
|
||||||
|
}
|
||||||
|
String peerStatePath = ZKUtil.joinZNode(peerZnode,
|
||||||
|
getConf().get("zookeeper.znode.replication.peers.state", "peer-state"));
|
||||||
|
if (ZKUtil.checkExists(zkw, peerStatePath) != -1) {
|
||||||
|
data = ZKUtil.getData(zkw, peerStatePath);
|
||||||
|
if (ProtobufUtil.isPBMagicPrefix(data)) continue;
|
||||||
|
migratePeerStateToPB(zkw, data, peerStatePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void migrateClusterKeyToPB(ZooKeeperWatcher zkw, String peerZnode, byte[] data)
|
||||||
|
throws KeeperException, NoNodeException {
|
||||||
|
ReplicationPeer peer = ZooKeeperProtos.ReplicationPeer.newBuilder()
|
||||||
|
.setClusterkey(Bytes.toString(data)).build();
|
||||||
|
ZKUtil.setData(zkw, peerZnode, ProtobufUtil.prependPBMagic(peer.toByteArray()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void migratePeerStateToPB(ZooKeeperWatcher zkw, byte[] data,
|
||||||
|
String peerStatePath)
|
||||||
|
throws KeeperException, NoNodeException {
|
||||||
|
String state = Bytes.toString(data);
|
||||||
|
if (ZooKeeperProtos.ReplicationState.State.ENABLED.name().equals(state)) {
|
||||||
|
ZKUtil.setData(zkw, peerStatePath, ReplicationStateZKBase.ENABLED_ZNODE_BYTES);
|
||||||
|
} else if (ZooKeeperProtos.ReplicationState.State.DISABLED.name().equals(state)) {
|
||||||
|
ZKUtil.setData(zkw, peerStatePath, ReplicationStateZKBase.DISABLED_ZNODE_BYTES);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String args[]) throws Exception {
|
||||||
|
System.exit(ToolRunner.run(HBaseConfiguration.create(), new ZKDataMigrator(), args));
|
||||||
|
}
|
||||||
|
|
||||||
|
static class ZKDataMigratorAbortable implements Abortable {
|
||||||
|
private boolean aborted = false;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void abort(String why, Throwable e) {
|
||||||
|
LOG.error("Got aborted with reason: " + why + ", and error: " + e);
|
||||||
|
this.aborted = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAborted() {
|
||||||
|
return this.aborted;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -150,7 +150,7 @@ public class TestNamespaceUpgrade {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static File untar(final File testdir) throws IOException {
|
static File untar(final File testdir) throws IOException {
|
||||||
// Find the src data under src/test/data
|
// Find the src data under src/test/data
|
||||||
final String datafile = "TestNamespaceUpgrade";
|
final String datafile = "TestNamespaceUpgrade";
|
||||||
File srcTarFile = new File(
|
File srcTarFile = new File(
|
||||||
|
|
|
@ -0,0 +1,249 @@
|
||||||
|
/**
|
||||||
|
* 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.migration;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
import org.apache.hadoop.conf.Configuration;
|
||||||
|
import org.apache.hadoop.fs.FileStatus;
|
||||||
|
import org.apache.hadoop.fs.FileSystem;
|
||||||
|
import org.apache.hadoop.fs.FsShell;
|
||||||
|
import org.apache.hadoop.fs.Path;
|
||||||
|
import org.apache.hadoop.hbase.HBaseTestingUtility;
|
||||||
|
import org.apache.hadoop.hbase.HConstants;
|
||||||
|
import org.apache.hadoop.hbase.MediumTests;
|
||||||
|
import org.apache.hadoop.hbase.master.TableNamespaceManager;
|
||||||
|
import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
|
||||||
|
import org.apache.hadoop.hbase.protobuf.generated.ZooKeeperProtos;
|
||||||
|
import org.apache.hadoop.hbase.protobuf.generated.ZooKeeperProtos.ReplicationPeer;
|
||||||
|
import org.apache.hadoop.hbase.protobuf.generated.ZooKeeperProtos.Table.State;
|
||||||
|
import org.apache.hadoop.hbase.regionserver.HRegionFileSystem;
|
||||||
|
import org.apache.hadoop.hbase.util.Bytes;
|
||||||
|
import org.apache.hadoop.hbase.util.FSUtils;
|
||||||
|
import org.apache.hadoop.hbase.util.HFileV1Detector;
|
||||||
|
import org.apache.hadoop.hbase.util.ZKDataMigrator;
|
||||||
|
import org.apache.hadoop.hbase.zookeeper.ZKTableReadOnly;
|
||||||
|
import org.apache.hadoop.hbase.zookeeper.ZKUtil;
|
||||||
|
import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher;
|
||||||
|
import org.apache.hadoop.util.ToolRunner;
|
||||||
|
import org.apache.zookeeper.KeeperException;
|
||||||
|
import org.apache.zookeeper.server.ZKDatabase;
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.AfterClass;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.BeforeClass;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.experimental.categories.Category;
|
||||||
|
import org.mockito.Mockito;
|
||||||
|
|
||||||
|
import com.google.protobuf.InvalidProtocolBufferException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Upgrade to 0.96 involves detecting HFileV1 in existing cluster, updating namespace and
|
||||||
|
* updating znodes. This class tests for HFileV1 detection and upgrading znodes.
|
||||||
|
* Uprading namespace is tested in {@link TestNamespaceUpgrade}.
|
||||||
|
*/
|
||||||
|
@Category(MediumTests.class)
|
||||||
|
public class TestUpgradeTo96 {
|
||||||
|
|
||||||
|
static final Log LOG = LogFactory.getLog(TestUpgradeTo96.class);
|
||||||
|
private final static HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* underlying file system instance
|
||||||
|
*/
|
||||||
|
private static FileSystem fs;
|
||||||
|
/**
|
||||||
|
* hbase root dir
|
||||||
|
*/
|
||||||
|
private static Path hbaseRootDir;
|
||||||
|
private static ZooKeeperWatcher zkw;
|
||||||
|
/**
|
||||||
|
* replication peer znode (/hbase/replication/peers)
|
||||||
|
*/
|
||||||
|
private static String replicationPeerZnode;
|
||||||
|
/**
|
||||||
|
* znode of a table
|
||||||
|
*/
|
||||||
|
private static String tableAZnode;
|
||||||
|
private static ReplicationPeer peer1;
|
||||||
|
/**
|
||||||
|
* znode for replication peer1 (/hbase/replication/peers/1)
|
||||||
|
*/
|
||||||
|
private static String peer1Znode;
|
||||||
|
|
||||||
|
@BeforeClass
|
||||||
|
public static void setUpBeforeClass() throws Exception {
|
||||||
|
// Start up the mini cluster on top of an 0.94 root.dir that has data from
|
||||||
|
// a 0.94 hbase run and see if we can migrate to 0.96
|
||||||
|
TEST_UTIL.startMiniZKCluster();
|
||||||
|
TEST_UTIL.startMiniDFSCluster(1);
|
||||||
|
|
||||||
|
hbaseRootDir = TEST_UTIL.getDefaultRootDirPath();
|
||||||
|
fs = FileSystem.get(TEST_UTIL.getConfiguration());
|
||||||
|
FSUtils.setRootDir(TEST_UTIL.getConfiguration(), hbaseRootDir);
|
||||||
|
zkw = TEST_UTIL.getZooKeeperWatcher();
|
||||||
|
|
||||||
|
Path testdir = TEST_UTIL.getDataTestDir("TestUpgradeTo96");
|
||||||
|
// get the untar 0.94 file structure
|
||||||
|
|
||||||
|
set94FSLayout(testdir);
|
||||||
|
setUp94Znodes();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lays out 0.94 file system layout using {@link TestNamespaceUpgrade} apis.
|
||||||
|
* @param testdir
|
||||||
|
* @throws IOException
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
private static void set94FSLayout(Path testdir) throws IOException, Exception {
|
||||||
|
File untar = TestNamespaceUpgrade.untar(new File(testdir.toString()));
|
||||||
|
if (!fs.exists(hbaseRootDir.getParent())) {
|
||||||
|
// mkdir at first
|
||||||
|
fs.mkdirs(hbaseRootDir.getParent());
|
||||||
|
}
|
||||||
|
FsShell shell = new FsShell(TEST_UTIL.getConfiguration());
|
||||||
|
shell.run(new String[] { "-put", untar.toURI().toString(), hbaseRootDir.toString() });
|
||||||
|
// See whats in minihdfs.
|
||||||
|
shell.run(new String[] { "-lsr", "/" });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets znodes used in 0.94 version. Only table and replication znodes will be upgraded to PB,
|
||||||
|
* others would be deleted.
|
||||||
|
* @throws KeeperException
|
||||||
|
*/
|
||||||
|
private static void setUp94Znodes() throws IOException, KeeperException {
|
||||||
|
// add some old znodes, which would be deleted after upgrade.
|
||||||
|
String rootRegionServerZnode = ZKUtil.joinZNode(zkw.baseZNode, "root-region-server");
|
||||||
|
ZKUtil.createWithParents(zkw, rootRegionServerZnode);
|
||||||
|
ZKUtil.createWithParents(zkw, zkw.backupMasterAddressesZNode);
|
||||||
|
// add table znode, data of its children would be protobuffized
|
||||||
|
tableAZnode = ZKUtil.joinZNode(zkw.tableZNode, "a");
|
||||||
|
ZKUtil.createWithParents(zkw, tableAZnode,
|
||||||
|
Bytes.toBytes(ZooKeeperProtos.Table.State.ENABLED.toString()));
|
||||||
|
// add replication znodes, data of its children would be protobuffized
|
||||||
|
String replicationZnode = ZKUtil.joinZNode(zkw.baseZNode, "replication");
|
||||||
|
replicationPeerZnode = ZKUtil.joinZNode(replicationZnode, "peers");
|
||||||
|
peer1Znode = ZKUtil.joinZNode(replicationPeerZnode, "1");
|
||||||
|
peer1 = ReplicationPeer.newBuilder().setClusterkey("abc:123:/hbase").build();
|
||||||
|
ZKUtil.createWithParents(zkw, peer1Znode, Bytes.toBytes(peer1.getClusterkey()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests a 0.94 filesystem for any HFileV1.
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testHFileV1Detector() throws Exception {
|
||||||
|
assertEquals(0, ToolRunner.run(TEST_UTIL.getConfiguration(), new HFileV1Detector(), null));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a corrupt file, and run HFileV1 detector tool
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testHFileV1DetectorWithCorruptFiles() throws Exception {
|
||||||
|
// add a corrupt file.
|
||||||
|
Path tablePath = new Path(hbaseRootDir, "foo");
|
||||||
|
FileStatus[] regionsDir = fs.listStatus(tablePath);
|
||||||
|
if (regionsDir == null) throw new IOException("No Regions found for table " + "foo");
|
||||||
|
Path columnFamilyDir = null;
|
||||||
|
Path targetRegion = null;
|
||||||
|
for (FileStatus s : regionsDir) {
|
||||||
|
if (fs.exists(new Path(s.getPath(), HRegionFileSystem.REGION_INFO_FILE))) {
|
||||||
|
targetRegion = s.getPath();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
FileStatus[] cfs = fs.listStatus(targetRegion);
|
||||||
|
for (FileStatus f : cfs) {
|
||||||
|
if (f.isDir()) {
|
||||||
|
columnFamilyDir = f.getPath();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LOG.debug("target columnFamilyDir: " + columnFamilyDir);
|
||||||
|
// now insert a corrupt file in the columnfamily.
|
||||||
|
Path corruptFile = new Path(columnFamilyDir, "corrupt_file");
|
||||||
|
if (!fs.createNewFile(corruptFile)) throw new IOException("Couldn't create corrupt file: "
|
||||||
|
+ corruptFile);
|
||||||
|
assertEquals(1, ToolRunner.run(TEST_UTIL.getConfiguration(), new HFileV1Detector(), null));
|
||||||
|
// remove the corrupt file
|
||||||
|
FileSystem.get(TEST_UTIL.getConfiguration()).delete(corruptFile, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testADirForHFileV1() throws Exception {
|
||||||
|
Path tablePath = new Path(hbaseRootDir, "foo");
|
||||||
|
System.out.println("testADirForHFileV1: " + tablePath.makeQualified(fs));
|
||||||
|
System.out.println("Passed: " + hbaseRootDir + "/foo");
|
||||||
|
assertEquals(0,
|
||||||
|
ToolRunner.run(TEST_UTIL.getConfiguration(), new HFileV1Detector(), new String[] { "-p"
|
||||||
|
+ "foo" }));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testZnodeMigration() throws Exception {
|
||||||
|
String rootRSZnode = ZKUtil.joinZNode(zkw.baseZNode, "root-region-server");
|
||||||
|
assertTrue(ZKUtil.checkExists(zkw, rootRSZnode) > -1);
|
||||||
|
ToolRunner.run(TEST_UTIL.getConfiguration(), new UpgradeTo96(), new String[] { "-execute" });
|
||||||
|
assertEquals(-1, ZKUtil.checkExists(zkw, rootRSZnode));
|
||||||
|
byte[] data = ZKUtil.getData(zkw, tableAZnode);
|
||||||
|
assertTrue(ProtobufUtil.isPBMagicPrefix(data));
|
||||||
|
checkTableState(data, ZooKeeperProtos.Table.State.ENABLED);
|
||||||
|
// ensure replication znodes are there, and protobuffed.
|
||||||
|
data = ZKUtil.getData(zkw, peer1Znode);
|
||||||
|
assertTrue(ProtobufUtil.isPBMagicPrefix(data));
|
||||||
|
checkReplicationPeerData(data, peer1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkTableState(byte[] data, State expectedState)
|
||||||
|
throws InvalidProtocolBufferException {
|
||||||
|
ZooKeeperProtos.Table.Builder builder = ZooKeeperProtos.Table.newBuilder();
|
||||||
|
int magicLen = ProtobufUtil.lengthOfPBMagic();
|
||||||
|
ZooKeeperProtos.Table t = builder.mergeFrom(data, magicLen, data.length - magicLen).build();
|
||||||
|
assertTrue(t.getState() == expectedState);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkReplicationPeerData(byte[] data, ReplicationPeer peer)
|
||||||
|
throws InvalidProtocolBufferException {
|
||||||
|
int magicLen = ProtobufUtil.lengthOfPBMagic();
|
||||||
|
ZooKeeperProtos.ReplicationPeer.Builder builder = ZooKeeperProtos.ReplicationPeer.newBuilder();
|
||||||
|
assertEquals(builder.mergeFrom(data, magicLen, data.length - magicLen).build().getClusterkey(),
|
||||||
|
peer.getClusterkey());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterClass
|
||||||
|
public static void tearDownAfterClass() throws Exception {
|
||||||
|
TEST_UTIL.shutdownMiniHBaseCluster();
|
||||||
|
TEST_UTIL.shutdownMiniDFSCluster();
|
||||||
|
TEST_UTIL.shutdownMiniZKCluster();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue