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 404f8ff34ba..c4f0e258cb7 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 @@ -195,11 +195,33 @@ public class RestoreSnapshotHelper { // this instance, by removing the regions already present in the restore dir. Set regionNames = new HashSet<>(regionManifests.keySet()); + List tableRegions = getTableRegions(); + RegionInfo mobRegion = MobUtils.getMobRegionInfo(snapshotManifest.getTableDescriptor() .getTableName()); + if (tableRegions != null) { + // restore the mob region in case + if (regionNames.contains(mobRegion.getEncodedName())) { + monitor.rethrowException(); + status.setStatus("Restoring mob region..."); + List mobRegions = new ArrayList<>(1); + mobRegions.add(mobRegion); + restoreHdfsMobRegions(exec, regionManifests, mobRegions); + regionNames.remove(mobRegion.getEncodedName()); + status.setStatus("Finished restoring mob region."); + } + } + if (regionNames.contains(mobRegion.getEncodedName())) { + // add the mob region + monitor.rethrowException(); + status.setStatus("Cloning mob region..."); + cloneHdfsMobRegion(regionManifests, mobRegion); + regionNames.remove(mobRegion.getEncodedName()); + status.setStatus("Finished cloning mob region."); + } + // Identify which region are still available and which not. // NOTE: we rely upon the region name as: "table name, start key, end key" - List tableRegions = getTableRegions(); if (tableRegions != null) { monitor.rethrowException(); for (RegionInfo regionInfo: tableRegions) { @@ -213,50 +235,40 @@ public class RestoreSnapshotHelper { metaChanges.addRegionToRemove(regionInfo); } } - - // Restore regions using the snapshot data - monitor.rethrowException(); - status.setStatus("Restoring table regions..."); - if (regionNames.contains(mobRegion.getEncodedName())) { - // restore the mob region in case - List mobRegions = new ArrayList<>(1); - mobRegions.add(mobRegion); - restoreHdfsMobRegions(exec, regionManifests, mobRegions); - regionNames.remove(mobRegion.getEncodedName()); - } - restoreHdfsRegions(exec, regionManifests, metaChanges.getRegionsToRestore()); - status.setStatus("Finished restoring all table regions."); - - // Remove regions from the current table - monitor.rethrowException(); - status.setStatus("Starting to delete excess regions from table"); - removeHdfsRegions(exec, metaChanges.getRegionsToRemove()); - status.setStatus("Finished deleting excess regions from table."); } // Regions to Add: present in the snapshot but not in the current table + List regionsToAdd = new ArrayList<>(regionNames.size()); if (regionNames.size() > 0) { - List regionsToAdd = new ArrayList<>(regionNames.size()); - monitor.rethrowException(); - // add the mob region - if (regionNames.contains(mobRegion.getEncodedName())) { - cloneHdfsMobRegion(regionManifests, mobRegion); - regionNames.remove(mobRegion.getEncodedName()); - } for (String regionName: regionNames) { LOG.info("region to add: " + regionName); - regionsToAdd.add(ProtobufUtil.toRegionInfo(regionManifests.get(regionName).getRegionInfo())); + regionsToAdd.add(ProtobufUtil.toRegionInfo(regionManifests.get(regionName) + .getRegionInfo())); } - - // Create new regions cloning from the snapshot - monitor.rethrowException(); - status.setStatus("Cloning regions..."); - RegionInfo[] clonedRegions = cloneHdfsRegions(exec, regionManifests, regionsToAdd); - metaChanges.setNewRegions(clonedRegions); - status.setStatus("Finished cloning regions."); } + // Create new regions cloning from the snapshot + // HBASE-19980: We need to call cloneHdfsRegions() before restoreHdfsRegions() because + // regionsMap is constructed in cloneHdfsRegions() and it can be used in restoreHdfsRegions(). + monitor.rethrowException(); + status.setStatus("Cloning regions..."); + RegionInfo[] clonedRegions = cloneHdfsRegions(exec, regionManifests, regionsToAdd); + metaChanges.setNewRegions(clonedRegions); + status.setStatus("Finished cloning regions."); + + // Restore regions using the snapshot data + monitor.rethrowException(); + status.setStatus("Restoring table regions..."); + restoreHdfsRegions(exec, regionManifests, metaChanges.getRegionsToRestore()); + status.setStatus("Finished restoring all table regions."); + + // Remove regions from the current table + monitor.rethrowException(); + status.setStatus("Starting to delete excess regions from table"); + removeHdfsRegions(exec, metaChanges.getRegionsToRemove()); + status.setStatus("Finished deleting excess regions from table."); + LOG.info("finishing restore table regions using snapshot=" + snapshotDesc); return metaChanges; @@ -742,11 +754,16 @@ public class RestoreSnapshotHelper { // Add the daughter region to the map String regionName = Bytes.toString(regionsMap.get(regionInfo.getEncodedNameAsBytes())); + if (regionName == null) { + regionName = regionInfo.getEncodedName(); + } LOG.debug("Restore reference " + regionName + " to " + clonedRegionName); synchronized (parentsMap) { Pair daughters = parentsMap.get(clonedRegionName); if (daughters == null) { - daughters = new Pair<>(regionName, null); + // In case one side of the split is already compacted, regionName is put as both first and + // second of Pair + daughters = new Pair<>(regionName, regionName); parentsMap.put(clonedRegionName, daughters); } else if (!regionName.equals(daughters.getFirst())) { daughters.setSecond(regionName); diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestRestoreSnapshotFromClient.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestRestoreSnapshotFromClient.java index 2556bec2cce..eb8b20e124f 100644 --- a/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestRestoreSnapshotFromClient.java +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestRestoreSnapshotFromClient.java @@ -23,6 +23,7 @@ import static org.junit.Assert.fail; import java.io.IOException; import java.util.HashSet; +import java.util.List; import java.util.Set; import org.apache.hadoop.conf.Configuration; @@ -295,6 +296,25 @@ public class TestRestoreSnapshotFromClient { } } + @Test + public void testRestoreSnapshotAfterSplittingRegions() throws IOException, InterruptedException { + List regionInfos = admin.getRegions(tableName); + RegionReplicaUtil.removeNonDefaultRegions(regionInfos); + + // Split the first region + splitRegion(regionInfos.get(0)); + + // Take a snapshot + admin.snapshot(snapshotName1, tableName); + + // Restore the snapshot + admin.disableTable(tableName); + admin.restoreSnapshot(snapshotName1); + admin.enableTable(tableName); + + verifyRowCount(TEST_UTIL, tableName, snapshot1Rows); + } + // ========================================================================== // Helpers // ==========================================================================