Skip to content

Commit 1525ec5

Browse files
author
Hernan Gelaf-Romer
committed
Re-use splits from full backup
1 parent 84f4fb3 commit 1525ec5

File tree

13 files changed

+644
-74
lines changed

13 files changed

+644
-74
lines changed

hbase-backup/src/main/java/org/apache/hadoop/hbase/backup/BackupRestoreFactory.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import org.apache.hadoop.hbase.backup.mapreduce.MapReduceBackupCopyJob;
2222
import org.apache.hadoop.hbase.backup.mapreduce.MapReduceBackupMergeJob;
2323
import org.apache.hadoop.hbase.backup.mapreduce.MapReduceRestoreJob;
24+
import org.apache.hadoop.hbase.backup.mapreduce.MapReduceRestoreToOriginalSplitsJob;
2425
import org.apache.hadoop.util.ReflectionUtils;
2526
import org.apache.yetus.audience.InterfaceAudience;
2627

@@ -43,8 +44,13 @@ private BackupRestoreFactory() {
4344
* @return backup restore job instance
4445
*/
4546
public static RestoreJob getRestoreJob(Configuration conf) {
47+
Class<? extends RestoreJob> defaultCls =
48+
conf.getBoolean(RestoreJob.KEEP_ORIGINAL_SPLITS_OPT, false)
49+
? MapReduceRestoreToOriginalSplitsJob.class
50+
: MapReduceRestoreJob.class;
51+
4652
Class<? extends RestoreJob> cls =
47-
conf.getClass(HBASE_INCR_RESTORE_IMPL_CLASS, MapReduceRestoreJob.class, RestoreJob.class);
53+
conf.getClass(HBASE_INCR_RESTORE_IMPL_CLASS, defaultCls, RestoreJob.class);
4854
RestoreJob service = ReflectionUtils.newInstance(cls, conf);
4955
service.setConf(conf);
5056
return service;

hbase-backup/src/main/java/org/apache/hadoop/hbase/backup/RestoreJob.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@
3030

3131
@InterfaceAudience.Private
3232
public interface RestoreJob extends Configurable {
33+
34+
String KEEP_ORIGINAL_SPLITS_OPT = "keep_original_splits";
35+
3336
/**
3437
* Run restore operation
3538
* @param dirPaths path array of WAL log directories

hbase-backup/src/main/java/org/apache/hadoop/hbase/backup/RestoreRequest.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,11 @@ public Builder withOvewrite(boolean overwrite) {
6767
return this;
6868
}
6969

70+
public Builder withKeepOriginalSplits(boolean keepOriginalSplits) {
71+
request.setKeepOriginalSplits(keepOriginalSplits);
72+
return this;
73+
}
74+
7075
public RestoreRequest build() {
7176
return request;
7277
}
@@ -80,6 +85,8 @@ public RestoreRequest build() {
8085
private TableName[] toTables;
8186
private boolean overwrite = false;
8287

88+
private boolean keepOriginalSplits = false;
89+
8390
private RestoreRequest() {
8491
}
8592

@@ -145,4 +152,13 @@ private RestoreRequest setOverwrite(boolean overwrite) {
145152
this.overwrite = overwrite;
146153
return this;
147154
}
155+
156+
public boolean isKeepOriginalSplits() {
157+
return keepOriginalSplits;
158+
}
159+
160+
private RestoreRequest setKeepOriginalSplits(boolean keepOriginalSplits) {
161+
this.keepOriginalSplits = keepOriginalSplits;
162+
return this;
163+
}
148164
}

hbase-backup/src/main/java/org/apache/hadoop/hbase/backup/impl/IncrementalTableBackupClient.java

Lines changed: 99 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,9 @@
3030
import java.util.TreeMap;
3131
import org.apache.commons.lang3.StringUtils;
3232
import org.apache.hadoop.fs.FileSystem;
33+
import org.apache.hadoop.fs.LocatedFileStatus;
3334
import org.apache.hadoop.fs.Path;
35+
import org.apache.hadoop.fs.RemoteIterator;
3436
import org.apache.hadoop.hbase.TableName;
3537
import org.apache.hadoop.hbase.backup.BackupCopyJob;
3638
import org.apache.hadoop.hbase.backup.BackupInfo;
@@ -40,13 +42,16 @@
4042
import org.apache.hadoop.hbase.backup.BackupType;
4143
import org.apache.hadoop.hbase.backup.HBackupFileSystem;
4244
import org.apache.hadoop.hbase.backup.mapreduce.MapReduceBackupCopyJob;
45+
import org.apache.hadoop.hbase.backup.mapreduce.MapReduceHFileSplitterJob;
4346
import org.apache.hadoop.hbase.backup.util.BackupUtils;
4447
import org.apache.hadoop.hbase.client.Admin;
4548
import org.apache.hadoop.hbase.client.ColumnFamilyDescriptor;
4649
import org.apache.hadoop.hbase.client.Connection;
50+
import org.apache.hadoop.hbase.io.hfile.HFile;
4751
import org.apache.hadoop.hbase.mapreduce.WALPlayer;
4852
import org.apache.hadoop.hbase.snapshot.SnapshotDescriptionUtils;
4953
import org.apache.hadoop.hbase.snapshot.SnapshotManifest;
54+
import org.apache.hadoop.hbase.snapshot.SnapshotRegionLocator;
5055
import org.apache.hadoop.hbase.util.Bytes;
5156
import org.apache.hadoop.hbase.util.CommonFSUtils;
5257
import org.apache.hadoop.hbase.util.HFileArchiveUtil;
@@ -120,8 +125,6 @@ protected static int getIndex(TableName tbl, List<TableName> sTableList) {
120125
@SuppressWarnings("unchecked")
121126
protected List<byte[]> handleBulkLoad(List<TableName> sTableList) throws IOException {
122127
Map<byte[], List<Path>>[] mapForSrc = new Map[sTableList.size()];
123-
List<String> activeFiles = new ArrayList<>();
124-
List<String> archiveFiles = new ArrayList<>();
125128
Pair<Map<TableName, Map<String, Map<String, List<Pair<String, Boolean>>>>>, List<byte[]>> pair =
126129
backupManager.readBulkloadRows(sTableList);
127130
Map<TableName, Map<String, Map<String, List<Pair<String, Boolean>>>>> map = pair.getFirst();
@@ -136,6 +139,8 @@ protected List<byte[]> handleBulkLoad(List<TableName> sTableList) throws IOExcep
136139

137140
for (Map.Entry<TableName, Map<String, Map<String, List<Pair<String, Boolean>>>>> tblEntry : map
138141
.entrySet()) {
142+
List<String> activeFiles = new ArrayList<>();
143+
List<String> archiveFiles = new ArrayList<>();
139144
TableName srcTable = tblEntry.getKey();
140145

141146
int srcIdx = getIndex(srcTable, sTableList);
@@ -197,55 +202,56 @@ protected List<byte[]> handleBulkLoad(List<TableName> sTableList) throws IOExcep
197202
}
198203
}
199204
}
205+
mergeSplitBulkloads(activeFiles, archiveFiles, srcTable);
206+
incrementalCopyBulkloadHFiles(tgtFs, srcTable);
200207
}
201-
202-
copyBulkLoadedFiles(activeFiles, archiveFiles);
203-
204208
return pair.getSecond();
205209
}
206210

207-
private void copyBulkLoadedFiles(List<String> activeFiles, List<String> archiveFiles)
208-
throws IOException {
209-
try {
210-
// Enable special mode of BackupDistCp
211-
conf.setInt(MapReduceBackupCopyJob.NUMBER_OF_LEVELS_TO_PRESERVE_KEY, 5);
212-
// Copy active files
213-
String tgtDest = backupInfo.getBackupRootDir() + Path.SEPARATOR + backupInfo.getBackupId();
214-
int attempt = 1;
215-
while (activeFiles.size() > 0) {
216-
LOG.info("Copy " + activeFiles.size() + " active bulk loaded files. Attempt =" + attempt++);
217-
String[] toCopy = new String[activeFiles.size()];
218-
activeFiles.toArray(toCopy);
219-
// Active file can be archived during copy operation,
220-
// we need to handle this properly
221-
try {
222-
incrementalCopyHFiles(toCopy, tgtDest);
223-
break;
224-
} catch (IOException e) {
225-
// Check if some files got archived
226-
// Update active and archived lists
227-
// When file is being moved from active to archive
228-
// directory, the number of active files decreases
229-
int numOfActive = activeFiles.size();
230-
updateFileLists(activeFiles, archiveFiles);
231-
if (activeFiles.size() < numOfActive) {
232-
continue;
233-
}
234-
// if not - throw exception
235-
throw e;
211+
private void mergeSplitBulkloads(List<String> activeFiles, List<String> archiveFiles,
212+
TableName tn) throws IOException {
213+
int attempt = 1;
214+
215+
while (!activeFiles.isEmpty()) {
216+
LOG.info("MergeSplit {} active bulk loaded files. Attempt={}", activeFiles.size(), attempt++);
217+
// Active file can be archived during copy operation,
218+
// we need to handle this properly
219+
try {
220+
mergeSplitBulkloads(activeFiles, tn);
221+
break;
222+
} catch (IOException e) {
223+
int numActiveFiles = activeFiles.size();
224+
updateFileLists(activeFiles, archiveFiles);
225+
if (activeFiles.size() < numActiveFiles) {
226+
continue;
236227
}
228+
229+
throw e;
237230
}
238-
// If incremental copy will fail for archived files
239-
// we will have partially loaded files in backup destination (only files from active data
240-
// directory). It is OK, because the backup will marked as FAILED and data will be cleaned up
241-
if (archiveFiles.size() > 0) {
242-
String[] toCopy = new String[archiveFiles.size()];
243-
archiveFiles.toArray(toCopy);
244-
incrementalCopyHFiles(toCopy, tgtDest);
231+
}
232+
233+
if (!archiveFiles.isEmpty()) {
234+
mergeSplitBulkloads(archiveFiles, tn);
235+
}
236+
}
237+
238+
private void mergeSplitBulkloads(List<String> files, TableName tn) throws IOException {
239+
MapReduceHFileSplitterJob player = new MapReduceHFileSplitterJob();
240+
conf.set(MapReduceHFileSplitterJob.BULK_OUTPUT_CONF_KEY,
241+
getBulkOutputDirForTable(tn).toString());
242+
player.setConf(conf);
243+
244+
String inputDirs = StringUtils.join(files, ",");
245+
String[] args = { inputDirs, tn.getNameAsString() };
246+
247+
try {
248+
int result = player.run(args);
249+
if (result != 0) {
250+
throw new RuntimeException("Failed to run MapReduceHFileSplitterJob");
245251
}
246-
} finally {
247-
// Disable special mode of BackupDistCp
248-
conf.unset(MapReduceBackupCopyJob.NUMBER_OF_LEVELS_TO_PRESERVE_KEY);
252+
} catch (Exception e) {
253+
LOG.error(e.toString(), e);
254+
throw new IOException(e);
249255
}
250256
}
251257

@@ -290,6 +296,7 @@ public void execute() throws IOException, ColumnFamilyMismatchException {
290296
try {
291297
// copy out the table and region info files for each table
292298
BackupUtils.copyTableRegionInfo(conn, backupInfo, conf);
299+
setupRegionLocator();
293300
// convert WAL to HFiles and copy them to .tmp under BACKUP_ROOT
294301
convertWALsToHFiles();
295302
incrementalCopyHFiles(new String[] { getBulkOutputDir().toString() },
@@ -337,6 +344,7 @@ protected void incrementalCopyHFiles(String[] files, String backupDest) throws I
337344
try {
338345
LOG.debug("Incremental copy HFiles is starting. dest=" + backupDest);
339346
// set overall backup phase: incremental_copy
347+
// TODO - This should now happen elsewhere maybe
340348
backupInfo.setPhase(BackupPhase.INCREMENTAL_COPY);
341349
// get incremental backup file list and prepare parms for DistCp
342350
String[] strArr = new String[files.length + 1];
@@ -431,6 +439,29 @@ protected void walToHFiles(List<String> dirPaths, List<String> tableList) throws
431439
}
432440
}
433441

442+
private void incrementalCopyBulkloadHFiles(FileSystem tgtFs, TableName tn) throws IOException {
443+
Path bulkOutDir = getBulkOutputDirForTable(tn);
444+
FileSystem fs = FileSystem.get(conf);
445+
446+
if (fs.exists(bulkOutDir)) {
447+
conf.setInt(MapReduceBackupCopyJob.NUMBER_OF_LEVELS_TO_PRESERVE_KEY, 2);
448+
Path tgtPath = getTargetDirForTable(tn);
449+
try {
450+
RemoteIterator<LocatedFileStatus> locatedFiles = tgtFs.listFiles(bulkOutDir, true);
451+
List<String> files = new ArrayList<>();
452+
while (locatedFiles.hasNext()) {
453+
LocatedFileStatus file = locatedFiles.next();
454+
if (file.isFile() && HFile.isHFileFormat(tgtFs, file.getPath())) {
455+
files.add(file.getPath().toString());
456+
}
457+
}
458+
incrementalCopyHFiles(files.toArray(files.toArray(new String[0])), tgtPath.toString());
459+
} finally {
460+
conf.unset(MapReduceBackupCopyJob.NUMBER_OF_LEVELS_TO_PRESERVE_KEY);
461+
}
462+
}
463+
}
464+
434465
protected Path getBulkOutputDirForTable(TableName table) {
435466
Path tablePath = getBulkOutputDir();
436467
tablePath = new Path(tablePath, table.getNamespaceAsString());
@@ -446,6 +477,30 @@ protected Path getBulkOutputDir() {
446477
return path;
447478
}
448479

480+
private Path getTargetDirForTable(TableName table) {
481+
Path path = new Path(backupInfo.getBackupRootDir() + Path.SEPARATOR + backupInfo.getBackupId());
482+
path = new Path(path, table.getNamespaceAsString());
483+
path = new Path(path, table.getNameAsString());
484+
return path;
485+
}
486+
487+
private void setupRegionLocator() throws IOException {
488+
Map<TableName, String> fullBackupIds = getFullBackupIds();
489+
try (BackupAdminImpl backupAdmin = new BackupAdminImpl(conn)) {
490+
491+
for (TableName tableName : backupInfo.getTables()) {
492+
String fullBackupId = fullBackupIds.get(tableName);
493+
BackupInfo fullBackupInfo = backupAdmin.getBackupInfo(fullBackupId);
494+
String snapshotName = fullBackupInfo.getSnapshotName(tableName);
495+
Path root = HBackupFileSystem.getTableBackupPath(tableName,
496+
new Path(fullBackupInfo.getBackupRootDir()), fullBackupId);
497+
String manifestDir =
498+
SnapshotDescriptionUtils.getCompletedSnapshotDir(snapshotName, root).toString();
499+
SnapshotRegionLocator.setSnapshotManifestDir(conf, manifestDir, tableName);
500+
}
501+
}
502+
}
503+
449504
private Map<TableName, String> getFullBackupIds() throws IOException {
450505
// Ancestors are stored from newest to oldest, so we can iterate backwards
451506
// in order to populate our backupId map with the most recent full backup

hbase-backup/src/main/java/org/apache/hadoop/hbase/backup/impl/RestoreTablesClient.java

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ public class RestoreTablesClient {
5959
private Path restoreRootDir;
6060
private boolean isOverwrite;
6161

62+
private boolean isKeepOriginalSplits;
63+
6264
public RestoreTablesClient(Connection conn, RestoreRequest request) throws IOException {
6365
this.backupRootDir = request.getBackupRootDir();
6466
this.backupId = request.getBackupId();
@@ -68,6 +70,7 @@ public RestoreTablesClient(Connection conn, RestoreRequest request) throws IOExc
6870
this.tTableArray = sTableArray;
6971
}
7072
this.isOverwrite = request.isOverwrite();
73+
this.isKeepOriginalSplits = request.isKeepOriginalSplits();
7174
this.conn = conn;
7275
this.conf = conn.getConfiguration();
7376
if (request.getRestoreRootDir() != null) {
@@ -132,7 +135,7 @@ private void checkTargetTables(TableName[] tTableArray, boolean isOverwrite) thr
132135
*/
133136

134137
private void restoreImages(BackupImage[] images, TableName sTable, TableName tTable,
135-
boolean truncateIfExists) throws IOException {
138+
boolean truncateIfExists, boolean isKeepOriginalSplits) throws IOException {
136139
// First image MUST be image of a FULL backup
137140
BackupImage image = images[0];
138141
String rootDir = image.getRootDir();
@@ -148,7 +151,7 @@ private void restoreImages(BackupImage[] images, TableName sTable, TableName tTa
148151
+ tableBackupPath.toString());
149152
conf.set(JOB_NAME_CONF_KEY, "Full_Restore-" + backupId + "-" + tTable);
150153
restoreTool.fullRestoreTable(conn, tableBackupPath, sTable, tTable, truncateIfExists,
151-
lastIncrBackupId);
154+
isKeepOriginalSplits, lastIncrBackupId);
152155
conf.unset(JOB_NAME_CONF_KEY);
153156
} else { // incremental Backup
154157
throw new IOException("Unexpected backup type " + image.getType());
@@ -183,7 +186,7 @@ private void restoreImages(BackupImage[] images, TableName sTable, TableName tTa
183186
dirList.toArray(paths);
184187
conf.set(JOB_NAME_CONF_KEY, "Incremental_Restore-" + backupId + "-" + tTable);
185188
restoreTool.incrementalRestoreTable(conn, tableBackupPath, paths, new TableName[] { sTable },
186-
new TableName[] { tTable }, lastIncrBackupId);
189+
new TableName[] { tTable }, lastIncrBackupId, isKeepOriginalSplits);
187190
LOG.info(sTable + " has been successfully restored to " + tTable);
188191
}
189192

@@ -208,7 +211,7 @@ private List<Path> getFilesRecursively(String fileBackupDir)
208211
* @throws IOException exception
209212
*/
210213
private void restore(BackupManifest manifest, TableName[] sTableArray, TableName[] tTableArray,
211-
boolean isOverwrite) throws IOException {
214+
boolean isOverwrite, boolean isKeepOriginalSplits) throws IOException {
212215
TreeSet<BackupImage> restoreImageSet = new TreeSet<>();
213216

214217
for (int i = 0; i < sTableArray.length; i++) {
@@ -223,7 +226,7 @@ private void restore(BackupManifest manifest, TableName[] sTableArray, TableName
223226
set.addAll(depList);
224227
BackupImage[] arr = new BackupImage[set.size()];
225228
set.toArray(arr);
226-
restoreImages(arr, table, tTableArray[i], isOverwrite);
229+
restoreImages(arr, table, tTableArray[i], isOverwrite, isKeepOriginalSplits);
227230
restoreImageSet.addAll(list);
228231
if (restoreImageSet != null && !restoreImageSet.isEmpty()) {
229232
LOG.info("Restore includes the following image(s):");
@@ -257,6 +260,6 @@ public void execute() throws IOException {
257260
Path rootPath = new Path(backupRootDir);
258261
BackupManifest manifest = HBackupFileSystem.getManifest(conf, rootPath, backupId);
259262

260-
restore(manifest, sTableArray, tTableArray, isOverwrite);
263+
restore(manifest, sTableArray, tTableArray, isOverwrite, isKeepOriginalSplits);
261264
}
262265
}

hbase-backup/src/main/java/org/apache/hadoop/hbase/backup/mapreduce/MapReduceHFileSplitterJob.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import org.apache.hadoop.hbase.mapreduce.HFileInputFormat;
3636
import org.apache.hadoop.hbase.mapreduce.HFileOutputFormat2;
3737
import org.apache.hadoop.hbase.mapreduce.TableMapReduceUtil;
38+
import org.apache.hadoop.hbase.snapshot.SnapshotRegionLocator;
3839
import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
3940
import org.apache.hadoop.hbase.util.MapReduceExtendedCell;
4041
import org.apache.hadoop.io.NullWritable;
@@ -118,7 +119,7 @@ public Job createSubmittableJob(String[] args) throws IOException {
118119
job.setMapOutputValueClass(MapReduceExtendedCell.class);
119120
try (Connection conn = ConnectionFactory.createConnection(conf);
120121
Table table = conn.getTable(tableName);
121-
RegionLocator regionLocator = conn.getRegionLocator(tableName)) {
122+
RegionLocator regionLocator = getRegionLocator(conf, conn, tableName)) {
122123
HFileOutputFormat2.configureIncrementalLoad(job, table.getDescriptor(), regionLocator);
123124
}
124125
LOG.debug("success configuring load incremental job");
@@ -171,4 +172,13 @@ public int run(String[] args) throws Exception {
171172
int result = job.waitForCompletion(true) ? 0 : 1;
172173
return result;
173174
}
175+
176+
private static RegionLocator getRegionLocator(Configuration conf, Connection conn,
177+
TableName table) throws IOException {
178+
if (SnapshotRegionLocator.shouldUseSnapshotRegionLocator(conf, table)) {
179+
return SnapshotRegionLocator.create(conf, table);
180+
}
181+
182+
return conn.getRegionLocator(table);
183+
}
174184
}

0 commit comments

Comments
 (0)