diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/HTableDescriptor.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/HTableDescriptor.java index 19bc2e7..a7ef13a 100644 --- a/hbase-client/src/main/java/org/apache/hadoop/hbase/HTableDescriptor.java +++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/HTableDescriptor.java @@ -354,8 +354,21 @@ public class HTableDescriptor implements Comparable { * @param desc The descriptor. */ public HTableDescriptor(final HTableDescriptor desc) { + this(desc.name, desc); + } + + /** + * Construct a table descriptor by cloning the descriptor passed as a parameter + * but using a different table name. + *

+ * Makes a deep copy of the supplied descriptor. + * Can make a modifiable descriptor from an UnmodifyableHTableDescriptor. + * @param name Table name. + * @param desc The descriptor. + */ + public HTableDescriptor(final TableName name, final HTableDescriptor desc) { super(); - setName(desc.name); + setName(name); setMetaFlags(this.name); for (HColumnDescriptor c: desc.families.values()) { this.families.put(c.getName(), new HColumnDescriptor(c)); diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/client/TableSnapshotScanner.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/client/TableSnapshotScanner.java index f817e70..64019e9 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/client/TableSnapshotScanner.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/client/TableSnapshotScanner.java @@ -34,11 +34,7 @@ import org.apache.hadoop.hbase.HRegionInfo; import org.apache.hadoop.hbase.HTableDescriptor; import org.apache.hadoop.hbase.classification.InterfaceAudience; import org.apache.hadoop.hbase.classification.InterfaceStability; -import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.SnapshotDescription; -import org.apache.hadoop.hbase.protobuf.generated.SnapshotProtos.SnapshotRegionManifest; import org.apache.hadoop.hbase.snapshot.RestoreSnapshotHelper; -import org.apache.hadoop.hbase.snapshot.SnapshotDescriptionUtils; -import org.apache.hadoop.hbase.snapshot.SnapshotManifest; import org.apache.hadoop.hbase.util.FSUtils; /** @@ -49,8 +45,8 @@ import org.apache.hadoop.hbase.util.FSUtils; *

* This also allows one to run the scan from an * online or offline hbase cluster. The snapshot files can be exported by using the - * {@link org.apache.hadoop.hbase.snapshot.ExportSnapshot} tool, - * to a pure-hdfs cluster, and this scanner can be used to + * {@link org.apache.hadoop.hbase.snapshot.ExportSnapshot} tool, + * to a pure-hdfs cluster, and this scanner can be used to * run the scan directly over the snapshot files. The snapshot should not be deleted while there * are open scanners reading from snapshot files. * @@ -79,6 +75,7 @@ public class TableSnapshotScanner extends AbstractClientScanner { private FileSystem fs; private Path rootDir; private Path restoreDir; + private String restoreId; private Scan scan; private ArrayList regions; private HTableDescriptor htd; @@ -118,30 +115,22 @@ public class TableSnapshotScanner extends AbstractClientScanner { this.snapshotName = snapshotName; this.rootDir = rootDir; // restoreDir will be deleted in close(), use a unique sub directory - this.restoreDir = new Path(restoreDir, UUID.randomUUID().toString()); + this.restoreId = UUID.randomUUID().toString(); + this.restoreDir = new Path(restoreDir, restoreId); this.scan = scan; this.fs = rootDir.getFileSystem(conf); init(); } private void init() throws IOException { - Path snapshotDir = SnapshotDescriptionUtils.getCompletedSnapshotDir(snapshotName, rootDir); - SnapshotDescription snapshotDesc = SnapshotDescriptionUtils.readSnapshotInfo(fs, snapshotDir); - SnapshotManifest manifest = SnapshotManifest.open(conf, fs, snapshotDir, snapshotDesc); - - // load table descriptor - htd = manifest.getTableDescriptor(); - - List regionManifests = manifest.getRegionManifests(); - if (regionManifests == null) { - throw new IllegalArgumentException("Snapshot seems empty"); - } - - regions = new ArrayList(regionManifests.size()); - for (SnapshotRegionManifest regionManifest : regionManifests) { - // load region descriptor - HRegionInfo hri = HRegionInfo.convert(regionManifest.getRegionInfo()); - + final RestoreSnapshotHelper.RestoreMetaChanges meta = + RestoreSnapshotHelper.copySnapshotForScanner( + conf, fs, rootDir, restoreDir, snapshotName, restoreId); + final List restoredRegions = meta.getRegionsToAdd(); + + htd = meta.getTableDescriptor(); + regions = new ArrayList(restoredRegions.size()); + for (HRegionInfo hri: restoredRegions) { if (CellUtil.overlappingKeys(scan.getStartRow(), scan.getStopRow(), hri.getStartKey(), hri.getEndKey())) { regions.add(hri); @@ -150,11 +139,7 @@ public class TableSnapshotScanner extends AbstractClientScanner { // sort for regions according to startKey. Collections.sort(regions); - initScanMetrics(scan); - - RestoreSnapshotHelper.copySnapshotForScanner(conf, fs, - rootDir, restoreDir, snapshotName); } @Override @@ -184,7 +169,7 @@ public class TableSnapshotScanner extends AbstractClientScanner { if (result == null) { currentRegionScanner.close(); currentRegionScanner = null; - } + } } } } diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/mapreduce/MultiTableSnapshotInputFormatImpl.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/mapreduce/MultiTableSnapshotInputFormatImpl.java index 5c46f2a..ec0abff 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/mapreduce/MultiTableSnapshotInputFormatImpl.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/mapreduce/MultiTableSnapshotInputFormatImpl.java @@ -26,6 +26,7 @@ import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.FileSystem; import org.apache.hadoop.fs.Path; import org.apache.hadoop.hbase.HRegionInfo; +import org.apache.hadoop.hbase.TableName; import org.apache.hadoop.hbase.classification.InterfaceAudience; import org.apache.hadoop.hbase.classification.InterfaceStability; import org.apache.hadoop.hbase.client.Scan; @@ -102,15 +103,18 @@ public class MultiTableSnapshotInputFormatImpl { String snapshotName = entry.getKey(); Path restoreDir = snapshotsToRestoreDirs.get(snapshotName); + String restoreId = restoreDir.getName(); // uh... how can we get the restoreId? SnapshotManifest manifest = TableSnapshotInputFormatImpl.getSnapshotManifest(conf, snapshotName, rootDir, fs); List regionInfos = - TableSnapshotInputFormatImpl.getRegionInfosFromManifest(manifest); + TableSnapshotInputFormatImpl.getRegionInfosFromManifest(manifest, + TableName.valueOf(restoreId)); for (Scan scan : entry.getValue()) { List splits = - TableSnapshotInputFormatImpl.getSplits(scan, manifest, regionInfos, restoreDir, conf); + TableSnapshotInputFormatImpl.getSplits(scan, manifest, regionInfos, restoreDir, + restoreId, conf); rtn.addAll(splits); } } @@ -214,8 +218,8 @@ public class MultiTableSnapshotInputFormatImpl { Map rtn = Maps.newHashMap(); for (String snapshotName : snapshots) { - Path restoreSnapshotDir = - new Path(baseRestoreDir, snapshotName + "__" + UUID.randomUUID().toString()); + String restoreId = snapshotName + "__" + UUID.randomUUID().toString(); + Path restoreSnapshotDir = new Path(baseRestoreDir, restoreId); rtn.put(snapshotName, restoreSnapshotDir); } @@ -244,9 +248,10 @@ public class MultiTableSnapshotInputFormatImpl { } } - void restoreSnapshot(Configuration conf, String snapshotName, Path rootDir, Path restoreDir, - FileSystem fs) throws IOException { - RestoreSnapshotHelper.copySnapshotForScanner(conf, fs, rootDir, restoreDir, snapshotName); + void restoreSnapshot(Configuration conf, String snapshotName, Path rootDir, + Path restoreDir, FileSystem fs) throws IOException { + RestoreSnapshotHelper.copySnapshotForScanner(conf, fs, rootDir, restoreDir, snapshotName, + restoreDir.getName()); // uh... how can we get the restoreId? } } diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/mapreduce/TableSnapshotInputFormatImpl.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/mapreduce/TableSnapshotInputFormatImpl.java index 1dfbfd3..bc1da95 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/mapreduce/TableSnapshotInputFormatImpl.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/mapreduce/TableSnapshotInputFormatImpl.java @@ -29,6 +29,7 @@ import org.apache.hadoop.hbase.HDFSBlocksDistribution; import org.apache.hadoop.hbase.HDFSBlocksDistribution.HostAndWeight; import org.apache.hadoop.hbase.HRegionInfo; import org.apache.hadoop.hbase.HTableDescriptor; +import org.apache.hadoop.hbase.TableName; import org.apache.hadoop.hbase.classification.InterfaceAudience; import org.apache.hadoop.hbase.classification.InterfaceStability; import org.apache.hadoop.hbase.client.ClientSideRegionScanner; @@ -70,6 +71,9 @@ public class TableSnapshotInputFormatImpl { // key for specifying the root dir of the restored snapshot protected static final String RESTORE_DIR_KEY = "hbase.TableSnapshotInputFormat.restore.dir"; + private static final String RESTORE_TABLE_NAME = + "hbase.TableSnapshotInputFormat.restore.table.name"; + /** See {@link #getBestLocations(Configuration, HDFSBlocksDistribution)} */ private static final String LOCALITY_CUTOFF_MULTIPLIER = "hbase.tablesnapshotinputformat.locality.cutoff.multiplier"; @@ -254,8 +258,8 @@ public class TableSnapshotInputFormatImpl { FileSystem fs = rootDir.getFileSystem(conf); SnapshotManifest manifest = getSnapshotManifest(conf, snapshotName, rootDir, fs); - - List regionInfos = getRegionInfosFromManifest(manifest); + TableName restoreTable = TableName.valueOf(conf.get(RESTORE_TABLE_NAME)); + List regionInfos = getRegionInfosFromManifest(manifest, restoreTable); // TODO: mapred does not support scan as input API. Work around for now. Scan scan = extractScanFromConf(conf); @@ -265,21 +269,24 @@ public class TableSnapshotInputFormatImpl { return getSplits(scan, manifest, regionInfos, restoreDir, conf); } - public static List getRegionInfosFromManifest(SnapshotManifest manifest) { + public static List getRegionInfosFromManifest(SnapshotManifest manifest, + TableName restoreTable) { List regionManifests = manifest.getRegionManifests(); if (regionManifests == null) { throw new IllegalArgumentException("Snapshot seems empty"); } + // sadness, how can we get the HRI generated from RestoreHelper.RestoreMetaChanges in setInput() List regionInfos = Lists.newArrayListWithCapacity(regionManifests.size()); - for (SnapshotRegionManifest regionManifest : regionManifests) { - regionInfos.add(HRegionInfo.convert(regionManifest.getRegionInfo())); + HRegionInfo hri = RestoreSnapshotHelper.cloneRegionInfo(restoreTable, + HRegionInfo.convert(regionManifest.getRegionInfo())); + regionInfos.add(hri); } return regionInfos; } - public static SnapshotManifest getSnapshotManifest(Configuration conf, String snapshotName, + static SnapshotManifest getSnapshotManifest(Configuration conf, String snapshotName, Path rootDir, FileSystem fs) throws IOException { Path snapshotDir = SnapshotDescriptionUtils.getCompletedSnapshotDir(snapshotName, rootDir); SnapshotDescription snapshotDesc = SnapshotDescriptionUtils.readSnapshotInfo(fs, snapshotDir); @@ -305,8 +312,16 @@ public class TableSnapshotInputFormatImpl { public static List getSplits(Scan scan, SnapshotManifest manifest, List regionManifests, Path restoreDir, Configuration conf) throws IOException { + return getSplits(scan, manifest, regionManifests, restoreDir, + conf.get(RESTORE_TABLE_NAME), conf); + } + + public static List getSplits(Scan scan, SnapshotManifest manifest, + List regionManifests, Path restoreDir, String restoreId, Configuration conf) + throws IOException { // load table descriptor - HTableDescriptor htd = manifest.getTableDescriptor(); + HTableDescriptor htd = new HTableDescriptor( + TableName.valueOf(restoreId), manifest.getTableDescriptor()); Path tableDir = FSUtils.getTableDir(restoreDir, htd.getTableName()); @@ -328,7 +343,6 @@ public class TableSnapshotInputFormatImpl { } return splits; - } /** @@ -392,16 +406,17 @@ public class TableSnapshotInputFormatImpl { */ public static void setInput(Configuration conf, String snapshotName, Path restoreDir) throws IOException { - conf.set(SNAPSHOT_NAME_KEY, snapshotName); - Path rootDir = FSUtils.getRootDir(conf); FileSystem fs = rootDir.getFileSystem(conf); - restoreDir = new Path(restoreDir, UUID.randomUUID().toString()); + String restoreId = UUID.randomUUID().toString(); + restoreDir = new Path(restoreDir, restoreId); + conf.set(SNAPSHOT_NAME_KEY, snapshotName); + conf.set(RESTORE_TABLE_NAME, restoreId); + conf.set(RESTORE_DIR_KEY, restoreDir.toString()); // TODO: restore from record readers to parallelize. - RestoreSnapshotHelper.copySnapshotForScanner(conf, fs, rootDir, restoreDir, snapshotName); - - conf.set(RESTORE_DIR_KEY, restoreDir.toString()); + RestoreSnapshotHelper.copySnapshotForScanner(conf, fs, rootDir, restoreDir, + snapshotName, restoreId); } } diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/snapshot/SnapshotManager.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/snapshot/SnapshotManager.java index 20cbcc7..155b9e0 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/snapshot/SnapshotManager.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/snapshot/SnapshotManager.java @@ -750,7 +750,7 @@ public class SnapshotManager extends MasterProcedureManager implements Stoppable cpHost.postRestoreSnapshot(reqSnapshot, snapshotTableDesc); } } else { - HTableDescriptor htd = RestoreSnapshotHelper.cloneTableSchema(snapshotTableDesc, tableName); + HTableDescriptor htd = new HTableDescriptor(tableName, snapshotTableDesc); if (cpHost != null) { cpHost.preCloneSnapshot(reqSnapshot, htd); } @@ -770,7 +770,7 @@ public class SnapshotManager extends MasterProcedureManager implements Stoppable } } } - + private void checkAndUpdateNamespaceQuota(SnapshotManifest manifest, TableName tableName) throws IOException { if (this.master.getMasterQuotaManager().isQuotaEnabled()) { diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/snapshot/RestoreSnapshotHelper.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/snapshot/RestoreSnapshotHelper.java index 1c684fb..7264635 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/snapshot/RestoreSnapshotHelper.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/snapshot/RestoreSnapshotHelper.java @@ -183,7 +183,7 @@ public class RestoreSnapshotHelper { return null; } - RestoreMetaChanges metaChanges = new RestoreMetaChanges(parentsMap); + RestoreMetaChanges metaChanges = new RestoreMetaChanges(tableDesc, parentsMap); // Take a copy of the manifest.keySet() since we are going to modify // this instance, by removing the regions already present in the restore dir. @@ -259,13 +259,19 @@ public class RestoreSnapshotHelper { */ public static class RestoreMetaChanges { private final Map > parentsMap; + private final HTableDescriptor htd; private List regionsToRestore = null; private List regionsToRemove = null; private List regionsToAdd = null; - RestoreMetaChanges(final Map > parentsMap) { + RestoreMetaChanges(HTableDescriptor htd, Map > parentsMap) { this.parentsMap = parentsMap; + this.htd = htd; + } + + public HTableDescriptor getTableDescriptor() { + return htd; } /** @@ -743,7 +749,11 @@ public class RestoreSnapshotHelper { * @return the new HRegion instance */ public HRegionInfo cloneRegionInfo(final HRegionInfo snapshotRegionInfo) { - HRegionInfo regionInfo = new HRegionInfo(tableDesc.getTableName(), + return cloneRegionInfo(tableDesc.getTableName(), snapshotRegionInfo); + } + + public static HRegionInfo cloneRegionInfo(TableName tableName, HRegionInfo snapshotRegionInfo) { + HRegionInfo regionInfo = new HRegionInfo(tableName, snapshotRegionInfo.getStartKey(), snapshotRegionInfo.getEndKey(), snapshotRegionInfo.isSplit(), snapshotRegionInfo.getRegionId()); regionInfo.setOffline(snapshotRegionInfo.isOffline()); @@ -769,40 +779,17 @@ public class RestoreSnapshotHelper { } /** - * Create a new table descriptor cloning the snapshot table schema. - * - * @param snapshotTableDescriptor - * @param tableName - * @return cloned table descriptor - * @throws IOException - */ - public static HTableDescriptor cloneTableSchema(final HTableDescriptor snapshotTableDescriptor, - final TableName tableName) throws IOException { - HTableDescriptor htd = new HTableDescriptor(tableName); - for (HColumnDescriptor hcd: snapshotTableDescriptor.getColumnFamilies()) { - htd.addFamily(hcd); - } - for (Map.Entry e: - snapshotTableDescriptor.getValues().entrySet()) { - htd.setValue(e.getKey(), e.getValue()); - } - for (Map.Entry e: snapshotTableDescriptor.getConfiguration().entrySet()) { - htd.setConfiguration(e.getKey(), e.getValue()); - } - return htd; - } - - /** * Copy the snapshot files for a snapshot scanner, discards meta changes. * @param conf * @param fs * @param rootDir * @param restoreDir * @param snapshotName + * @param restoreName * @throws IOException */ - public static void copySnapshotForScanner(Configuration conf, FileSystem fs, Path rootDir, - Path restoreDir, String snapshotName) throws IOException { + public static RestoreMetaChanges copySnapshotForScanner(Configuration conf, FileSystem fs, + Path rootDir, Path restoreDir, String snapshotName, String restoreName) throws IOException { // ensure that restore dir is not under root dir if (!restoreDir.getFileSystem(conf).getUri().equals(rootDir.getFileSystem(conf).getUri())) { throw new IllegalArgumentException("Filesystems for restore directory and HBase root " + @@ -823,13 +810,16 @@ public class RestoreSnapshotHelper { // we send createBackRefs=false so that restored hfiles do not create back reference links // in the base hbase root dir. + TableName tableName = TableName.valueOf(restoreName); + HTableDescriptor htd = new HTableDescriptor(tableName, manifest.getTableDescriptor()); RestoreSnapshotHelper helper = new RestoreSnapshotHelper(conf, fs, - manifest, manifest.getTableDescriptor(), restoreDir, monitor, status, false); - helper.restoreHdfsRegions(); // TODO: parallelize. + manifest, htd, restoreDir, monitor, status, false); + RestoreMetaChanges metaChanges = helper.restoreHdfsRegions(); // TODO: parallelize. if (LOG.isDebugEnabled()) { LOG.debug("Restored table dir:" + restoreDir); FSUtils.logFileSystemState(fs, restoreDir, LOG); } + return metaChanges; } } diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/snapshot/TestRestoreSnapshotHelper.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/snapshot/TestRestoreSnapshotHelper.java index 3fac2fd..90c927a 100644 --- a/hbase-server/src/test/java/org/apache/hadoop/hbase/snapshot/TestRestoreSnapshotHelper.java +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/snapshot/TestRestoreSnapshotHelper.java @@ -76,6 +76,32 @@ public class TestRestoreSnapshotHelper { } @Test + public void testRestoreForScanner() throws Exception { + final String SNAPSHOT_NAME = "snapshot"; + SnapshotMock snapshotMock = new SnapshotMock(TEST_UTIL.getConfiguration(), fs, rootDir); + SnapshotMock.SnapshotBuilder builder = snapshotMock.createSnapshotV2(SNAPSHOT_NAME); + builder.addRegionV2(); + builder.addRegionV2(); + Path snapshotDir = builder.commit(); + + Path externalDir = TEST_UTIL.getDataTestDir("testMRRestore"); + + // Restore with same dir/same name + String r1name = "r1"; + String r2name = "r2"; + Path r1dir = new Path(externalDir, r1name); + Path r2dir = new Path(externalDir, r2name); + RestoreSnapshotHelper.copySnapshotForScanner(conf, fs, + rootDir, r1dir, SNAPSHOT_NAME, r1name); + RestoreSnapshotHelper.copySnapshotForScanner(conf, fs, + rootDir, r1dir, SNAPSHOT_NAME, r2name); + RestoreSnapshotHelper.copySnapshotForScanner(conf, fs, + rootDir, r2dir, SNAPSHOT_NAME, r1name); + RestoreSnapshotHelper.copySnapshotForScanner(conf, fs, + rootDir, r2dir, SNAPSHOT_NAME, r2name); + } + + @Test public void testRestore() throws IOException { // Test Rolling-Upgrade like Snapshot. // half machines writing using v1 and the others using v2 format.