All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.hadoop.hbase.migration.NamespaceUpgrade Maven / Gradle / Ivy

There is a newer version: 3.0.0-beta-1
Show newest version
/**
 * 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.migration;

import java.io.IOException;
import java.util.Arrays;
import java.util.Comparator;
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.fs.FSDataInputStream;
import org.apache.hadoop.fs.FileStatus;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.fs.PathFilter;
import org.apache.hadoop.hbase.Cell;
import org.apache.hadoop.hbase.CellUtil;
import org.apache.hadoop.hbase.HConstants;
import org.apache.hadoop.hbase.HRegionInfo;
import org.apache.hadoop.hbase.HTableDescriptor;
import org.apache.hadoop.hbase.NamespaceDescriptor;
import org.apache.hadoop.hbase.ServerName;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.MetaTableAccessor;
import org.apache.hadoop.hbase.client.Delete;
import org.apache.hadoop.hbase.client.Get;
import org.apache.hadoop.hbase.client.Put;
import org.apache.hadoop.hbase.client.Result;
import org.apache.hadoop.hbase.exceptions.DeserializationException;
import org.apache.hadoop.hbase.regionserver.HRegion;
import org.apache.hadoop.hbase.regionserver.HRegionFileSystem;
import org.apache.hadoop.hbase.wal.WAL;
import org.apache.hadoop.hbase.wal.WALFactory;
import org.apache.hadoop.hbase.security.access.AccessControlLists;
import org.apache.hadoop.hbase.snapshot.SnapshotDescriptionUtils;
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.hadoop.hbase.util.FSTableDescriptors;
import org.apache.hadoop.hbase.util.FSUtils;
import org.apache.hadoop.util.Tool;

import com.google.common.collect.Lists;
import com.google.common.primitives.Ints;

/**
 * Upgrades old 0.94 filesystem layout to namespace layout
 * Does the following:
 *
 * - creates system namespace directory and move .META. table there
 * renaming .META. table to hbase:meta,
 * this in turn would require to re-encode the region directory name
 *
 * 

The pre-0.96 paths and dir names are hardcoded in here. */ public class NamespaceUpgrade implements Tool { private static final Log LOG = LogFactory.getLog(NamespaceUpgrade.class); private Configuration conf; private FileSystem fs; private Path rootDir; private Path sysNsDir; private Path defNsDir; private Path baseDirs[]; private Path backupDir; // First move everything to this tmp .data dir in case there is a table named 'data' private static final String TMP_DATA_DIR = ".data"; // Old dir names to migrate. private static final String DOT_LOGS = ".logs"; private static final String DOT_OLD_LOGS = ".oldlogs"; private static final String DOT_CORRUPT = ".corrupt"; private static final String DOT_SPLITLOG = "splitlog"; private static final String DOT_ARCHIVE = ".archive"; // The old default directory of hbase.dynamic.jars.dir(0.94.12 release). private static final String DOT_LIB_DIR = ".lib"; private static final String OLD_ACL = "_acl_"; /** Directories that are not HBase table directories */ static final List NON_USER_TABLE_DIRS = Arrays.asList(new String[] { DOT_LOGS, DOT_OLD_LOGS, DOT_CORRUPT, DOT_SPLITLOG, HConstants.HBCK_SIDELINEDIR_NAME, DOT_ARCHIVE, HConstants.SNAPSHOT_DIR_NAME, HConstants.HBASE_TEMP_DIRECTORY, TMP_DATA_DIR, OLD_ACL, DOT_LIB_DIR}); public NamespaceUpgrade() throws IOException { super(); } public void init() throws IOException { this.rootDir = FSUtils.getRootDir(conf); FSUtils.setFsDefault(getConf(), rootDir); this.fs = FileSystem.get(conf); Path tmpDataDir = new Path(rootDir, TMP_DATA_DIR); sysNsDir = new Path(tmpDataDir, NamespaceDescriptor.SYSTEM_NAMESPACE_NAME_STR); defNsDir = new Path(tmpDataDir, NamespaceDescriptor.DEFAULT_NAMESPACE_NAME_STR); baseDirs = new Path[]{rootDir, new Path(rootDir, HConstants.HFILE_ARCHIVE_DIRECTORY), new Path(rootDir, HConstants.HBASE_TEMP_DIRECTORY)}; backupDir = new Path(rootDir, HConstants.MIGRATION_NAME); } public void upgradeTableDirs() throws IOException, DeserializationException { // if new version is written then upgrade is done if (verifyNSUpgrade(fs, rootDir)) { return; } makeNamespaceDirs(); migrateTables(); migrateSnapshots(); migrateDotDirs(); migrateMeta(); migrateACL(); deleteRoot(); FSUtils.setVersion(fs, rootDir); } /** * Remove the -ROOT- dir. No longer of use. * @throws IOException */ public void deleteRoot() throws IOException { Path rootDir = new Path(this.rootDir, "-ROOT-"); if (this.fs.exists(rootDir)) { if (!this.fs.delete(rootDir, true)) LOG.info("Failed remove of " + rootDir); LOG.info("Deleted " + rootDir); } } /** * Rename all the dot dirs -- .data, .archive, etc. -- as data, archive, etc.; i.e. minus the dot. * @throws IOException */ public void migrateDotDirs() throws IOException { // Dot dirs to rename. Leave the tmp dir named '.tmp' and snapshots as .hbase-snapshot. final Path archiveDir = new Path(rootDir, HConstants.HFILE_ARCHIVE_DIRECTORY); Path [][] dirs = new Path[][] { new Path [] {new Path(rootDir, DOT_CORRUPT), new Path(rootDir, HConstants.CORRUPT_DIR_NAME)}, new Path [] {new Path(rootDir, DOT_LOGS), new Path(rootDir, HConstants.HREGION_LOGDIR_NAME)}, new Path [] {new Path(rootDir, DOT_OLD_LOGS), new Path(rootDir, HConstants.HREGION_OLDLOGDIR_NAME)}, new Path [] {new Path(rootDir, TMP_DATA_DIR), new Path(rootDir, HConstants.BASE_NAMESPACE_DIR)}, new Path[] { new Path(rootDir, DOT_LIB_DIR), new Path(rootDir, HConstants.LIB_DIR)}}; for (Path [] dir: dirs) { Path src = dir[0]; Path tgt = dir[1]; if (!this.fs.exists(src)) { LOG.info("Does not exist: " + src); continue; } rename(src, tgt); } // Do the .archive dir. Need to move its subdirs to the default ns dir under data dir... so // from '.archive/foo', to 'archive/data/default/foo'. Path oldArchiveDir = new Path(rootDir, DOT_ARCHIVE); if (this.fs.exists(oldArchiveDir)) { // This is a pain doing two nn calls but portable over h1 and h2. mkdirs(archiveDir); Path archiveDataDir = new Path(archiveDir, HConstants.BASE_NAMESPACE_DIR); mkdirs(archiveDataDir); rename(oldArchiveDir, new Path(archiveDataDir, NamespaceDescriptor.DEFAULT_NAMESPACE_NAME_STR)); } // Update the system and user namespace dirs removing the dot in front of .data. Path dataDir = new Path(rootDir, HConstants.BASE_NAMESPACE_DIR); sysNsDir = new Path(dataDir, NamespaceDescriptor.SYSTEM_NAMESPACE_NAME_STR); defNsDir = new Path(dataDir, NamespaceDescriptor.DEFAULT_NAMESPACE_NAME_STR); } private void mkdirs(final Path p) throws IOException { if (!this.fs.mkdirs(p)) throw new IOException("Failed make of " + p); } private void rename(final Path src, final Path tgt) throws IOException { if (!fs.rename(src, tgt)) { throw new IOException("Failed move " + src + " to " + tgt); } } /** * Create the system and default namespaces dirs * @throws IOException */ public void makeNamespaceDirs() throws IOException { if (!fs.exists(sysNsDir)) { if (!fs.mkdirs(sysNsDir)) { throw new IOException("Failed to create system namespace dir: " + sysNsDir); } } if (!fs.exists(defNsDir)) { if (!fs.mkdirs(defNsDir)) { throw new IOException("Failed to create default namespace dir: " + defNsDir); } } } /** * Migrate all tables into respective namespaces, either default or system. We put them into * a temporary location, '.data', in case a user table is name 'data'. In a later method we will * move stuff from .data to data. * @throws IOException */ public void migrateTables() throws IOException { List sysTables = Lists.newArrayList("-ROOT-",".META.", ".META"); // Migrate tables including archive and tmp for (Path baseDir: baseDirs) { if (!fs.exists(baseDir)) continue; List oldTableDirs = FSUtils.getLocalTableDirs(fs, baseDir); for (Path oldTableDir: oldTableDirs) { if (NON_USER_TABLE_DIRS.contains(oldTableDir.getName())) continue; if (sysTables.contains(oldTableDir.getName())) continue; // Make the new directory under the ns to which we will move the table. Path nsDir = new Path(this.defNsDir, TableName.valueOf(oldTableDir.getName()).getQualifierAsString()); LOG.info("Moving " + oldTableDir + " to " + nsDir); if (!fs.exists(nsDir.getParent())) { if (!fs.mkdirs(nsDir.getParent())) { throw new IOException("Failed to create namespace dir "+nsDir.getParent()); } } if (sysTables.indexOf(oldTableDir.getName()) < 0) { LOG.info("Migrating table " + oldTableDir.getName() + " to " + nsDir); if (!fs.rename(oldTableDir, nsDir)) { throw new IOException("Failed to move "+oldTableDir+" to namespace dir "+nsDir); } } } } } public void migrateSnapshots() throws IOException { //migrate snapshot dir Path oldSnapshotDir = new Path(rootDir, HConstants.OLD_SNAPSHOT_DIR_NAME); Path newSnapshotDir = new Path(rootDir, HConstants.SNAPSHOT_DIR_NAME); if (fs.exists(oldSnapshotDir)) { boolean foundOldSnapshotDir = false; // Logic to verify old snapshot dir culled from SnapshotManager // ignore all the snapshots in progress FileStatus[] snapshots = fs.listStatus(oldSnapshotDir, new SnapshotDescriptionUtils.CompletedSnaphotDirectoriesFilter(fs)); // loop through all the completed snapshots for (FileStatus snapshot : snapshots) { Path info = new Path(snapshot.getPath(), SnapshotDescriptionUtils.SNAPSHOTINFO_FILE); // if the snapshot is bad if (fs.exists(info)) { foundOldSnapshotDir = true; break; } } if(foundOldSnapshotDir) { LOG.info("Migrating snapshot dir"); if (!fs.rename(oldSnapshotDir, newSnapshotDir)) { throw new IOException("Failed to move old snapshot dir "+ oldSnapshotDir+" to new "+newSnapshotDir); } } } } public void migrateMeta() throws IOException { Path newMetaDir = new Path(this.sysNsDir, TableName.META_TABLE_NAME.getQualifierAsString()); Path newMetaRegionDir = new Path(newMetaDir, HRegionInfo.FIRST_META_REGIONINFO.getEncodedName()); Path oldMetaDir = new Path(rootDir, ".META."); if (fs.exists(oldMetaDir)) { LOG.info("Migrating meta table " + oldMetaDir.getName() + " to " + newMetaDir); if (!fs.rename(oldMetaDir, newMetaDir)) { throw new IOException("Failed to migrate meta table " + oldMetaDir.getName() + " to " + newMetaDir); } } else { // on windows NTFS, meta's name is .META (note the missing dot at the end) oldMetaDir = new Path(rootDir, ".META"); if (fs.exists(oldMetaDir)) { LOG.info("Migrating meta table " + oldMetaDir.getName() + " to " + newMetaDir); if (!fs.rename(oldMetaDir, newMetaDir)) { throw new IOException("Failed to migrate meta table " + oldMetaDir.getName() + " to " + newMetaDir); } } } // Since meta table name has changed rename meta region dir from it's old encoding to new one Path oldMetaRegionDir = HRegion.getRegionDir(rootDir, new Path(newMetaDir, "1028785192").toString()); if (fs.exists(oldMetaRegionDir)) { LOG.info("Migrating meta region " + oldMetaRegionDir + " to " + newMetaRegionDir); if (!fs.rename(oldMetaRegionDir, newMetaRegionDir)) { throw new IOException("Failed to migrate meta region " + oldMetaRegionDir + " to " + newMetaRegionDir); } } // Remove .tableinfo files as they refer to ".META.". // They will be recreated by master on startup. removeTableInfoInPre96Format(TableName.META_TABLE_NAME); Path oldRootDir = new Path(rootDir, "-ROOT-"); if(!fs.rename(oldRootDir, backupDir)) { throw new IllegalStateException("Failed to old data: "+oldRootDir+" to "+backupDir); } } /** * Removes .tableinfo files that are laid in pre-96 format (i.e., the tableinfo files are under * table directory). * @param tableName * @throws IOException */ private void removeTableInfoInPre96Format(TableName tableName) throws IOException { Path tableDir = FSUtils.getTableDir(rootDir, tableName); FileStatus[] status = FSUtils.listStatus(fs, tableDir, TABLEINFO_PATHFILTER); if (status == null) return; for (FileStatus fStatus : status) { FSUtils.delete(fs, fStatus.getPath(), false); } } public void migrateACL() throws IOException { TableName oldTableName = TableName.valueOf(OLD_ACL); Path oldTablePath = new Path(rootDir, oldTableName.getNameAsString()); if(!fs.exists(oldTablePath)) { return; } LOG.info("Migrating ACL table"); TableName newTableName = AccessControlLists.ACL_TABLE_NAME; Path newTablePath = FSUtils.getTableDir(rootDir, newTableName); HTableDescriptor oldDesc = readTableDescriptor(fs, getCurrentTableInfoStatus(fs, oldTablePath)); if(FSTableDescriptors.getTableInfoPath(fs, newTablePath) == null) { LOG.info("Creating new tableDesc for ACL"); HTableDescriptor newDesc = new HTableDescriptor(oldDesc); newDesc.setName(newTableName); new FSTableDescriptors(this.conf).createTableDescriptorForTableDirectory( newTablePath, newDesc, true); } ServerName fakeServer = ServerName.valueOf("nsupgrade", 96, 123); final WALFactory walFactory = new WALFactory(conf, null, fakeServer.toString()); WAL metawal = walFactory.getMetaWAL(HRegionInfo.FIRST_META_REGIONINFO.getEncodedNameAsBytes()); FSTableDescriptors fst = new FSTableDescriptors(conf); HRegion meta = HRegion.openHRegion(rootDir, HRegionInfo.FIRST_META_REGIONINFO, fst.get(TableName.META_TABLE_NAME), metawal, conf); HRegion region = null; try { for(Path regionDir : FSUtils.getRegionDirs(fs, oldTablePath)) { LOG.info("Migrating ACL region "+regionDir.getName()); HRegionInfo oldRegionInfo = HRegionFileSystem.loadRegionInfoFileContent(fs, regionDir); HRegionInfo newRegionInfo = new HRegionInfo(newTableName, oldRegionInfo.getStartKey(), oldRegionInfo.getEndKey(), oldRegionInfo.isSplit(), oldRegionInfo.getRegionId()); newRegionInfo.setOffline(oldRegionInfo.isOffline()); region = new HRegion( HRegionFileSystem.openRegionFromFileSystem(conf, fs, oldTablePath, oldRegionInfo, false), metawal, conf, oldDesc, null); region.initialize(); updateAcls(region); // closing the region would flush it so we don't need an explicit flush to save // acl changes. region.close(); //Create new region dir Path newRegionDir = new Path(newTablePath, newRegionInfo.getEncodedName()); if(!fs.exists(newRegionDir)) { if(!fs.mkdirs(newRegionDir)) { throw new IllegalStateException("Failed to create new region dir: " + newRegionDir); } } //create new region info file, delete in case one exists HRegionFileSystem.openRegionFromFileSystem(conf, fs, newTablePath, newRegionInfo, false); //migrate region contents for(FileStatus file : fs.listStatus(regionDir, new FSUtils.UserTableDirFilter(fs))) { if(file.getPath().getName().equals(HRegionFileSystem.REGION_INFO_FILE)) continue; if(!fs.rename(file.getPath(), newRegionDir)) { throw new IllegalStateException("Failed to move file "+file.getPath()+" to " + newRegionDir); } } meta.put(MetaTableAccessor.makePutFromRegionInfo(newRegionInfo)); meta.delete(MetaTableAccessor.makeDeleteFromRegionInfo(oldRegionInfo)); } } finally { meta.flush(true); meta.waitForFlushesAndCompactions(); meta.close(); walFactory.close(); if(region != null) { region.close(); } } if(!fs.rename(oldTablePath, backupDir)) { throw new IllegalStateException("Failed to old data: "+oldTablePath+" to "+backupDir); } } /** * Deletes the old _acl_ entry, and inserts a new one using namespace. * @param region * @throws IOException */ void updateAcls(HRegion region) throws IOException { byte[] rowKey = Bytes.toBytes(NamespaceUpgrade.OLD_ACL); // get the old _acl_ entry, if present. Get g = new Get(rowKey); Result r = region.get(g); if (r != null && r.size() > 0) { // create a put for new _acl_ entry with rowkey as hbase:acl Put p = new Put(AccessControlLists.ACL_GLOBAL_NAME); for (Cell c : r.rawCells()) { p.addImmutable(CellUtil.cloneFamily(c), CellUtil.cloneQualifier(c), CellUtil.cloneValue(c)); } region.put(p); // delete the old entry Delete del = new Delete(rowKey); region.delete(del); } // delete the old entry for '-ROOT-' rowKey = Bytes.toBytes(TableName.OLD_ROOT_STR); Delete del = new Delete(rowKey); region.delete(del); // rename .META. to hbase:meta rowKey = Bytes.toBytes(TableName.OLD_META_STR); g = new Get(rowKey); r = region.get(g); if (r != null && r.size() > 0) { // create a put for new .META. entry with rowkey as hbase:meta Put p = new Put(TableName.META_TABLE_NAME.getName()); for (Cell c : r.rawCells()) { p.addImmutable(CellUtil.cloneFamily(c), CellUtil.cloneQualifier(c), CellUtil.cloneValue(c)); } region.put(p); // delete the old entry del = new Delete(rowKey); region.delete(del); } } //Culled from FSTableDescriptors private static HTableDescriptor readTableDescriptor(FileSystem fs, FileStatus status) throws IOException { int len = Ints.checkedCast(status.getLen()); byte [] content = new byte[len]; FSDataInputStream fsDataInputStream = fs.open(status.getPath()); try { fsDataInputStream.readFully(content); } finally { fsDataInputStream.close(); } HTableDescriptor htd = null; try { htd = HTableDescriptor.parseFrom(content); } catch (DeserializationException e) { throw new IOException("content=" + Bytes.toShort(content), e); } return htd; } private static final PathFilter TABLEINFO_PATHFILTER = new PathFilter() { @Override public boolean accept(Path p) { // Accept any file that starts with TABLEINFO_NAME return p.getName().startsWith(".tableinfo"); } }; static final Comparator TABLEINFO_FILESTATUS_COMPARATOR = new Comparator() { @Override public int compare(FileStatus left, FileStatus right) { return right.compareTo(left); }}; // logic culled from FSTableDescriptors static FileStatus getCurrentTableInfoStatus(FileSystem fs, Path dir) throws IOException { FileStatus [] status = FSUtils.listStatus(fs, dir, TABLEINFO_PATHFILTER); if (status == null || status.length < 1) return null; FileStatus mostCurrent = null; for (FileStatus file : status) { if (mostCurrent == null || TABLEINFO_FILESTATUS_COMPARATOR.compare(file, mostCurrent) < 0) { mostCurrent = file; } } return mostCurrent; } public static boolean verifyNSUpgrade(FileSystem fs, Path rootDir) throws IOException { try { return FSUtils.getVersion(fs, rootDir).equals(HConstants.FILE_SYSTEM_VERSION); } catch (DeserializationException e) { throw new IOException("Failed to verify namespace upgrade", e); } } @Override public int run(String[] args) throws Exception { if (args.length < 1 || !args[0].equals("--upgrade")) { System.out.println("Usage: --upgrade"); return 0; } init(); upgradeTableDirs(); return 0; } @Override public void setConf(Configuration conf) { this.conf = conf; } @Override public Configuration getConf() { return conf; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy