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 f0f1ba3899ae..6b40601d154b 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 @@ -638,26 +638,28 @@ private void cloneHdfsMobRegion(final Map region final RegionInfo region) throws IOException { // clone region info (change embedded tableName with the new one) Path clonedRegionPath = MobUtils.getMobRegionPath(rootDir, tableDesc.getTableName()); - cloneRegion(MobUtils.getMobRegionInfo(tableDesc.getTableName()), clonedRegionPath, region, - regionManifests.get(region.getEncodedName())); + cloneRegularOrMobRegion(MobUtils.getMobRegionInfo(tableDesc.getTableName()), clonedRegionPath, + region, regionManifests.get(region.getEncodedName())); } /** - * Clone region directory content from the snapshot info. Each region is encoded with the table - * name, so the cloned region will have a different region name. Instead of copying the hfiles a - * HFileLink is created. + * Clone region directory content from a snapshot manifest. This method is used for both regular + * regions and MOB regions. Instead of copying the hfiles a HFileLink is created. * @param regionDir {@link Path} cloned dir */ - private void cloneRegion(final RegionInfo newRegionInfo, final Path regionDir, + private void cloneRegularOrMobRegion(final RegionInfo newRegionInfo, final Path regionDir, final RegionInfo snapshotRegionInfo, final SnapshotRegionManifest manifest) throws IOException { final String tableName = tableDesc.getTableName().getNameAsString(); final String snapshotName = snapshotDesc.getName(); + final boolean isMobRegion = MobUtils.isMobRegionInfo(newRegionInfo); + final Path parentDir = + isMobRegion ? MobUtils.getMobTableDir(rootDir, tableDesc.getTableName()) : tableDir; for (SnapshotRegionManifest.FamilyFiles familyFiles : manifest.getFamilyFilesList()) { Path familyDir = new Path(regionDir, familyFiles.getFamilyName().toStringUtf8()); List clonedFiles = new ArrayList<>(); - HRegionFileSystem regionFS = (fs.exists(regionDir)) - ? HRegionFileSystem.openRegionFromFileSystem(conf, fs, tableDir, newRegionInfo, false) - : HRegionFileSystem.createRegionOnFileSystem(conf, fs, tableDir, newRegionInfo); + HRegionFileSystem regionFS = fs.exists(regionDir) + ? HRegionFileSystem.openRegionFromFileSystem(conf, fs, parentDir, newRegionInfo, false) + : HRegionFileSystem.createRegionOnFileSystem(conf, fs, parentDir, newRegionInfo); Configuration sftConf = StoreUtils.createStoreConfiguration(conf, tableDesc, tableDesc.getColumnFamily(familyFiles.getFamilyName().toByteArray())); @@ -675,7 +677,7 @@ private void cloneRegion(final RegionInfo newRegionInfo, final Path regionDir, for (SnapshotRegionManifest.StoreFile storeFile : familyFiles.getStoreFilesList()) { LOG.info("Adding HFileLink " + storeFile.getName() + " from cloned region " + "in snapshot " + snapshotName + " to table=" + tableName); - if (MobUtils.isMobRegionInfo(newRegionInfo)) { + if (isMobRegion) { String mobFileName = HFileLink.createHFileLinkName(snapshotRegionInfo, storeFile.getName()); Path mobPath = new Path(familyDir, mobFileName); @@ -704,8 +706,8 @@ private void cloneRegion(final RegionInfo newRegionInfo, final Path regionDir, */ private void cloneRegion(final HRegion region, final RegionInfo snapshotRegionInfo, final SnapshotRegionManifest manifest) throws IOException { - cloneRegion(region.getRegionInfo(), new Path(tableDir, region.getRegionInfo().getEncodedName()), - snapshotRegionInfo, manifest); + cloneRegularOrMobRegion(region.getRegionInfo(), + new Path(tableDir, region.getRegionInfo().getEncodedName()), snapshotRegionInfo, manifest); } /** diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/snapshot/TestMobRestoreSnapshotHelper.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/snapshot/TestMobRestoreSnapshotHelper.java index 380044b733fb..be26448bf9b8 100644 --- a/hbase-server/src/test/java/org/apache/hadoop/hbase/snapshot/TestMobRestoreSnapshotHelper.java +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/snapshot/TestMobRestoreSnapshotHelper.java @@ -17,15 +17,24 @@ */ package org.apache.hadoop.hbase.snapshot; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + import java.io.IOException; import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.fs.Path; +import org.apache.hadoop.hbase.HBaseTestingUtil; import org.apache.hadoop.hbase.TableName; import org.apache.hadoop.hbase.client.Table; import org.apache.hadoop.hbase.mob.MobConstants; +import org.apache.hadoop.hbase.mob.MobUtils; import org.apache.hadoop.hbase.snapshot.MobSnapshotTestingUtils.SnapshotMock; import org.apache.hadoop.hbase.testclassification.MediumTests; import org.apache.hadoop.hbase.util.Bytes; +import org.apache.hadoop.hbase.util.CommonFSUtils; import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -55,4 +64,47 @@ protected void createTableAndSnapshot(TableName tableName, String snapshotName) TEST_UTIL.loadTable(table, column); TEST_UTIL.getAdmin().snapshot(snapshotName, tableName); } + + @Test + public void testRestoreMobTableFromSnapshot() throws IOException { + TableName mobTableName = TableName.valueOf("testRestoreMobTable"); + String snapshotName = "testRestoreMobTable_snapshot"; + createTableAndSnapshot(mobTableName, snapshotName); + assertTrue(TEST_UTIL.getAdmin().tableExists(mobTableName)); + assertTrue(MobUtils.hasMobColumns(TEST_UTIL.getAdmin().getDescriptor(mobTableName))); + assertTrue(TEST_UTIL.getAdmin().listSnapshots().stream() + .anyMatch(snapshotDescription -> snapshotName.equals(snapshotDescription.getName()))); + + // Clone the snapshot to a new MOB table + TableName newMobTableName = TableName.valueOf("newTestRestoreMobTable"); + TEST_UTIL.getAdmin().cloneSnapshot(snapshotName, newMobTableName); + assertTrue(TEST_UTIL.getAdmin().tableExists(newMobTableName)); + assertTrue(MobUtils.hasMobColumns(TEST_UTIL.getAdmin().getDescriptor(newMobTableName))); + + Path hbaseRootDir = TEST_UTIL.getDefaultRootDirPath(); + Path mobRegionPath = MobUtils.getMobTableDir(hbaseRootDir, newMobTableName); + assertTrue(fs.exists(mobRegionPath)); + Path errorMobRegionPathInTableDir = + new Path(CommonFSUtils.getTableDir(hbaseRootDir, newMobTableName), + MobUtils.getMobRegionInfo(newMobTableName).getEncodedName()); + assertFalse(fs.exists(errorMobRegionPathInTableDir)); + + try (Table originMobTable = TEST_UTIL.getConnection().getTable(mobTableName); + Table clonedMobTable = TEST_UTIL.getConnection().getTable(newMobTableName)) { + assertEquals(HBaseTestingUtil.countRows(originMobTable), + HBaseTestingUtil.countRows(clonedMobTable)); + } + + // Delete the original MOB table and restore it from the snapshot + TEST_UTIL.deleteTable(mobTableName); + assertFalse(TEST_UTIL.getAdmin().tableExists(mobTableName)); + TEST_UTIL.getAdmin().cloneSnapshot(snapshotName, mobTableName); + assertTrue(TEST_UTIL.getAdmin().tableExists(mobTableName)); + assertTrue(MobUtils.hasMobColumns(TEST_UTIL.getAdmin().getDescriptor(mobTableName))); + mobRegionPath = MobUtils.getMobTableDir(hbaseRootDir, mobTableName); + assertTrue(fs.exists(mobRegionPath)); + errorMobRegionPathInTableDir = new Path(CommonFSUtils.getTableDir(hbaseRootDir, mobTableName), + MobUtils.getMobRegionInfo(mobTableName).getEncodedName()); + assertFalse(fs.exists(errorMobRegionPathInTableDir)); + } }