1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.hadoop.hbase.snapshot;
20
21 import java.io.IOException;
22 import java.io.InputStream;
23 import java.io.OutputStream;
24 import java.util.ArrayList;
25 import java.util.Arrays;
26 import java.util.HashMap;
27 import java.util.HashSet;
28 import java.util.LinkedList;
29 import java.util.List;
30 import java.util.Map;
31 import java.util.Set;
32 import java.util.TreeMap;
33 import java.util.concurrent.ThreadPoolExecutor;
34
35 import org.apache.commons.logging.Log;
36 import org.apache.commons.logging.LogFactory;
37 import org.apache.hadoop.hbase.classification.InterfaceAudience;
38 import org.apache.hadoop.conf.Configuration;
39 import org.apache.hadoop.fs.FileStatus;
40 import org.apache.hadoop.fs.FileSystem;
41 import org.apache.hadoop.fs.Path;
42 import org.apache.hadoop.hbase.HColumnDescriptor;
43 import org.apache.hadoop.hbase.HRegionInfo;
44 import org.apache.hadoop.hbase.HTableDescriptor;
45 import org.apache.hadoop.hbase.TableName;
46 import org.apache.hadoop.hbase.backup.HFileArchiver;
47 import org.apache.hadoop.hbase.MetaTableAccessor;
48 import org.apache.hadoop.hbase.client.Connection;
49 import org.apache.hadoop.hbase.errorhandling.ForeignExceptionDispatcher;
50 import org.apache.hadoop.hbase.io.HFileLink;
51 import org.apache.hadoop.hbase.io.Reference;
52 import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
53 import org.apache.hadoop.hbase.monitoring.MonitoredTask;
54 import org.apache.hadoop.hbase.monitoring.TaskMonitor;
55 import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.SnapshotDescription;
56 import org.apache.hadoop.hbase.protobuf.generated.SnapshotProtos.SnapshotRegionManifest;
57 import org.apache.hadoop.hbase.regionserver.HRegion;
58 import org.apache.hadoop.hbase.regionserver.HRegionFileSystem;
59 import org.apache.hadoop.hbase.regionserver.StoreFileInfo;
60 import org.apache.hadoop.hbase.util.Bytes;
61 import org.apache.hadoop.hbase.util.FSUtils;
62 import org.apache.hadoop.hbase.util.ModifyRegionUtils;
63 import org.apache.hadoop.hbase.util.Pair;
64 import org.apache.hadoop.io.IOUtils;
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107 @InterfaceAudience.Private
108 public class RestoreSnapshotHelper {
109 private static final Log LOG = LogFactory.getLog(RestoreSnapshotHelper.class);
110
111 private final Map<byte[], byte[]> regionsMap =
112 new TreeMap<byte[], byte[]>(Bytes.BYTES_COMPARATOR);
113
114 private final Map<String, Pair<String, String> > parentsMap =
115 new HashMap<String, Pair<String, String> >();
116
117 private final ForeignExceptionDispatcher monitor;
118 private final MonitoredTask status;
119
120 private final SnapshotManifest snapshotManifest;
121 private final SnapshotDescription snapshotDesc;
122 private final TableName snapshotTable;
123
124 private final HTableDescriptor tableDesc;
125 private final Path rootDir;
126 private final Path tableDir;
127
128 private final Configuration conf;
129 private final FileSystem fs;
130
131 public RestoreSnapshotHelper(final Configuration conf,
132 final FileSystem fs,
133 final SnapshotManifest manifest,
134 final HTableDescriptor tableDescriptor,
135 final Path rootDir,
136 final ForeignExceptionDispatcher monitor,
137 final MonitoredTask status)
138 {
139 this.fs = fs;
140 this.conf = conf;
141 this.snapshotManifest = manifest;
142 this.snapshotDesc = manifest.getSnapshotDescription();
143 this.snapshotTable = TableName.valueOf(snapshotDesc.getTable());
144 this.tableDesc = tableDescriptor;
145 this.rootDir = rootDir;
146 this.tableDir = FSUtils.getTableDir(rootDir, tableDesc.getTableName());
147 this.monitor = monitor;
148 this.status = status;
149 }
150
151
152
153
154
155 public RestoreMetaChanges restoreHdfsRegions() throws IOException {
156 ThreadPoolExecutor exec = SnapshotManifest.createExecutor(conf, "RestoreSnapshot");
157 try {
158 return restoreHdfsRegions(exec);
159 } finally {
160 exec.shutdown();
161 }
162 }
163
164 private RestoreMetaChanges restoreHdfsRegions(final ThreadPoolExecutor exec) throws IOException {
165 LOG.debug("starting restore");
166
167 Map<String, SnapshotRegionManifest> regionManifests = snapshotManifest.getRegionManifestsMap();
168 if (regionManifests == null) {
169 LOG.warn("Nothing to restore. Snapshot " + snapshotDesc + " looks empty");
170 return null;
171 }
172
173 RestoreMetaChanges metaChanges = new RestoreMetaChanges(parentsMap);
174
175
176
177 Set<String> regionNames = new HashSet<String>(regionManifests.keySet());
178
179
180
181 List<HRegionInfo> tableRegions = getTableRegions();
182 if (tableRegions != null) {
183 monitor.rethrowException();
184 for (HRegionInfo regionInfo: tableRegions) {
185 String regionName = regionInfo.getEncodedName();
186 if (regionNames.contains(regionName)) {
187 LOG.info("region to restore: " + regionName);
188 regionNames.remove(regionName);
189 metaChanges.addRegionToRestore(regionInfo);
190 } else {
191 LOG.info("region to remove: " + regionName);
192 metaChanges.addRegionToRemove(regionInfo);
193 }
194 }
195
196
197 monitor.rethrowException();
198 status.setStatus("Restoring table regions...");
199 restoreHdfsRegions(exec, regionManifests, metaChanges.getRegionsToRestore());
200 status.setStatus("Finished restoring all table regions.");
201
202
203 monitor.rethrowException();
204 status.setStatus("Starting to delete excess regions from table");
205 removeHdfsRegions(exec, metaChanges.getRegionsToRemove());
206 status.setStatus("Finished deleting excess regions from table.");
207 }
208
209
210 if (regionNames.size() > 0) {
211 List<HRegionInfo> regionsToAdd = new ArrayList<HRegionInfo>(regionNames.size());
212
213 monitor.rethrowException();
214 for (String regionName: regionNames) {
215 LOG.info("region to add: " + regionName);
216 regionsToAdd.add(HRegionInfo.convert(regionManifests.get(regionName).getRegionInfo()));
217 }
218
219
220 monitor.rethrowException();
221 status.setStatus("Cloning regions...");
222 HRegionInfo[] clonedRegions = cloneHdfsRegions(exec, regionManifests, regionsToAdd);
223 metaChanges.setNewRegions(clonedRegions);
224 status.setStatus("Finished cloning regions.");
225 }
226
227 return metaChanges;
228 }
229
230
231
232
233 public static class RestoreMetaChanges {
234 private final Map<String, Pair<String, String> > parentsMap;
235
236 private List<HRegionInfo> regionsToRestore = null;
237 private List<HRegionInfo> regionsToRemove = null;
238 private List<HRegionInfo> regionsToAdd = null;
239
240 RestoreMetaChanges(final Map<String, Pair<String, String> > parentsMap) {
241 this.parentsMap = parentsMap;
242 }
243
244
245
246
247 public boolean hasRegionsToAdd() {
248 return this.regionsToAdd != null && this.regionsToAdd.size() > 0;
249 }
250
251
252
253
254
255
256
257 public List<HRegionInfo> getRegionsToAdd() {
258 return this.regionsToAdd;
259 }
260
261
262
263
264 public boolean hasRegionsToRestore() {
265 return this.regionsToRestore != null && this.regionsToRestore.size() > 0;
266 }
267
268
269
270
271
272
273 public List<HRegionInfo> getRegionsToRestore() {
274 return this.regionsToRestore;
275 }
276
277
278
279
280 public boolean hasRegionsToRemove() {
281 return this.regionsToRemove != null && this.regionsToRemove.size() > 0;
282 }
283
284
285
286
287
288
289
290 public List<HRegionInfo> getRegionsToRemove() {
291 return this.regionsToRemove;
292 }
293
294 void setNewRegions(final HRegionInfo[] hris) {
295 if (hris != null) {
296 regionsToAdd = Arrays.asList(hris);
297 } else {
298 regionsToAdd = null;
299 }
300 }
301
302 void addRegionToRemove(final HRegionInfo hri) {
303 if (regionsToRemove == null) {
304 regionsToRemove = new LinkedList<HRegionInfo>();
305 }
306 regionsToRemove.add(hri);
307 }
308
309 void addRegionToRestore(final HRegionInfo hri) {
310 if (regionsToRestore == null) {
311 regionsToRestore = new LinkedList<HRegionInfo>();
312 }
313 regionsToRestore.add(hri);
314 }
315
316 public void updateMetaParentRegions(Connection connection,
317 final List<HRegionInfo> regionInfos) throws IOException {
318 if (regionInfos == null || parentsMap.isEmpty()) return;
319
320
321 Map<String, HRegionInfo> regionsByName = new HashMap<String, HRegionInfo>(regionInfos.size());
322 List<HRegionInfo> parentRegions = new LinkedList<>();
323 for (HRegionInfo regionInfo: regionInfos) {
324 if (regionInfo.isSplitParent()) {
325 parentRegions.add(regionInfo);
326 } else {
327 regionsByName.put(regionInfo.getEncodedName(), regionInfo);
328 }
329 }
330
331
332 for (HRegionInfo regionInfo: parentRegions) {
333 Pair<String, String> daughters = parentsMap.get(regionInfo.getEncodedName());
334 if (daughters == null) {
335
336
337 LOG.warn("Skip update of unreferenced offline parent: " + regionInfo);
338 continue;
339 }
340
341
342 if (daughters.getSecond() == null) {
343 daughters.setSecond(daughters.getFirst());
344 }
345
346 LOG.debug("Update splits parent " + regionInfo.getEncodedName() + " -> " + daughters);
347 MetaTableAccessor.addRegionToMeta(connection, regionInfo,
348 regionsByName.get(daughters.getFirst()),
349 regionsByName.get(daughters.getSecond()));
350 }
351 }
352 }
353
354
355
356
357 private void removeHdfsRegions(final ThreadPoolExecutor exec, final List<HRegionInfo> regions)
358 throws IOException {
359 if (regions == null || regions.size() == 0) return;
360 ModifyRegionUtils.editRegions(exec, regions, new ModifyRegionUtils.RegionEditTask() {
361 @Override
362 public void editRegion(final HRegionInfo hri) throws IOException {
363 HFileArchiver.archiveRegion(conf, fs, hri);
364 }
365 });
366 }
367
368
369
370
371 private void restoreHdfsRegions(final ThreadPoolExecutor exec,
372 final Map<String, SnapshotRegionManifest> regionManifests,
373 final List<HRegionInfo> regions) throws IOException {
374 if (regions == null || regions.size() == 0) return;
375 ModifyRegionUtils.editRegions(exec, regions, new ModifyRegionUtils.RegionEditTask() {
376 @Override
377 public void editRegion(final HRegionInfo hri) throws IOException {
378 restoreRegion(hri, regionManifests.get(hri.getEncodedName()));
379 }
380 });
381 }
382
383 private Map<String, List<SnapshotRegionManifest.StoreFile>> getRegionHFileReferences(
384 final SnapshotRegionManifest manifest) {
385 Map<String, List<SnapshotRegionManifest.StoreFile>> familyMap =
386 new HashMap<String, List<SnapshotRegionManifest.StoreFile>>(manifest.getFamilyFilesCount());
387 for (SnapshotRegionManifest.FamilyFiles familyFiles: manifest.getFamilyFilesList()) {
388 familyMap.put(familyFiles.getFamilyName().toStringUtf8(),
389 new ArrayList<SnapshotRegionManifest.StoreFile>(familyFiles.getStoreFilesList()));
390 }
391 return familyMap;
392 }
393
394
395
396
397
398 private void restoreRegion(final HRegionInfo regionInfo,
399 final SnapshotRegionManifest regionManifest) throws IOException {
400 Map<String, List<SnapshotRegionManifest.StoreFile>> snapshotFiles =
401 getRegionHFileReferences(regionManifest);
402
403 Path regionDir = new Path(tableDir, regionInfo.getEncodedName());
404 String tableName = tableDesc.getTableName().getNameAsString();
405
406
407 for (Path familyDir: FSUtils.getFamilyDirs(fs, regionDir)) {
408 byte[] family = Bytes.toBytes(familyDir.getName());
409 Set<String> familyFiles = getTableRegionFamilyFiles(familyDir);
410 List<SnapshotRegionManifest.StoreFile> snapshotFamilyFiles =
411 snapshotFiles.remove(familyDir.getName());
412 if (snapshotFamilyFiles != null) {
413 List<SnapshotRegionManifest.StoreFile> hfilesToAdd =
414 new ArrayList<SnapshotRegionManifest.StoreFile>();
415 for (SnapshotRegionManifest.StoreFile storeFile: snapshotFamilyFiles) {
416 if (familyFiles.contains(storeFile.getName())) {
417
418 familyFiles.remove(storeFile.getName());
419 } else {
420
421 hfilesToAdd.add(storeFile);
422 }
423 }
424
425
426 for (String hfileName: familyFiles) {
427 Path hfile = new Path(familyDir, hfileName);
428 LOG.trace("Removing hfile=" + hfileName +
429 " from region=" + regionInfo.getEncodedName() + " table=" + tableName);
430 HFileArchiver.archiveStoreFile(conf, fs, regionInfo, tableDir, family, hfile);
431 }
432
433
434 for (SnapshotRegionManifest.StoreFile storeFile: hfilesToAdd) {
435 LOG.debug("Adding HFileLink " + storeFile.getName() +
436 " to region=" + regionInfo.getEncodedName() + " table=" + tableName);
437 restoreStoreFile(familyDir, regionInfo, storeFile);
438 }
439 } else {
440
441 LOG.trace("Removing family=" + Bytes.toString(family) +
442 " from region=" + regionInfo.getEncodedName() + " table=" + tableName);
443 HFileArchiver.archiveFamily(fs, conf, regionInfo, tableDir, family);
444 fs.delete(familyDir, true);
445 }
446 }
447
448
449 for (Map.Entry<String, List<SnapshotRegionManifest.StoreFile>> familyEntry:
450 snapshotFiles.entrySet()) {
451 Path familyDir = new Path(regionDir, familyEntry.getKey());
452 if (!fs.mkdirs(familyDir)) {
453 throw new IOException("Unable to create familyDir=" + familyDir);
454 }
455
456 for (SnapshotRegionManifest.StoreFile storeFile: familyEntry.getValue()) {
457 LOG.trace("Adding HFileLink " + storeFile.getName() + " to table=" + tableName);
458 restoreStoreFile(familyDir, regionInfo, storeFile);
459 }
460 }
461 }
462
463
464
465
466 private Set<String> getTableRegionFamilyFiles(final Path familyDir) throws IOException {
467 Set<String> familyFiles = new HashSet<String>();
468
469 FileStatus[] hfiles = FSUtils.listStatus(fs, familyDir);
470 if (hfiles == null) return familyFiles;
471
472 for (FileStatus hfileRef: hfiles) {
473 String hfileName = hfileRef.getPath().getName();
474 familyFiles.add(hfileName);
475 }
476
477 return familyFiles;
478 }
479
480
481
482
483
484 private HRegionInfo[] cloneHdfsRegions(final ThreadPoolExecutor exec,
485 final Map<String, SnapshotRegionManifest> regionManifests,
486 final List<HRegionInfo> regions) throws IOException {
487 if (regions == null || regions.size() == 0) return null;
488
489 final Map<String, HRegionInfo> snapshotRegions =
490 new HashMap<String, HRegionInfo>(regions.size());
491
492
493 HRegionInfo[] clonedRegionsInfo = new HRegionInfo[regions.size()];
494 for (int i = 0; i < clonedRegionsInfo.length; ++i) {
495
496 HRegionInfo snapshotRegionInfo = regions.get(i);
497 clonedRegionsInfo[i] = cloneRegionInfo(snapshotRegionInfo);
498
499
500 String snapshotRegionName = snapshotRegionInfo.getEncodedName();
501 String clonedRegionName = clonedRegionsInfo[i].getEncodedName();
502 regionsMap.put(Bytes.toBytes(snapshotRegionName), Bytes.toBytes(clonedRegionName));
503 LOG.info("clone region=" + snapshotRegionName + " as " + clonedRegionName);
504
505
506 snapshotRegions.put(clonedRegionName, snapshotRegionInfo);
507 }
508
509
510 ModifyRegionUtils.createRegions(exec, conf, rootDir, tableDir,
511 tableDesc, clonedRegionsInfo, new ModifyRegionUtils.RegionFillTask() {
512 @Override
513 public void fillRegion(final HRegion region) throws IOException {
514 HRegionInfo snapshotHri = snapshotRegions.get(region.getRegionInfo().getEncodedName());
515 cloneRegion(region, snapshotHri, regionManifests.get(snapshotHri.getEncodedName()));
516 }
517 });
518
519 return clonedRegionsInfo;
520 }
521
522
523
524
525
526
527
528
529
530
531
532
533 private void cloneRegion(final HRegion region, final HRegionInfo snapshotRegionInfo,
534 final SnapshotRegionManifest manifest) throws IOException {
535 final Path regionDir = new Path(tableDir, region.getRegionInfo().getEncodedName());
536 final String tableName = tableDesc.getTableName().getNameAsString();
537 for (SnapshotRegionManifest.FamilyFiles familyFiles: manifest.getFamilyFilesList()) {
538 Path familyDir = new Path(regionDir, familyFiles.getFamilyName().toStringUtf8());
539 for (SnapshotRegionManifest.StoreFile storeFile: familyFiles.getStoreFilesList()) {
540 LOG.info("Adding HFileLink " + storeFile.getName() + " to table=" + tableName);
541 restoreStoreFile(familyDir, snapshotRegionInfo, storeFile);
542 }
543 }
544 }
545
546
547
548
549
550
551
552
553
554
555
556
557
558 private void restoreStoreFile(final Path familyDir, final HRegionInfo regionInfo,
559 final SnapshotRegionManifest.StoreFile storeFile) throws IOException {
560 String hfileName = storeFile.getName();
561 if (HFileLink.isHFileLink(hfileName)) {
562 HFileLink.createFromHFileLink(conf, fs, familyDir, hfileName);
563 } else if (StoreFileInfo.isReference(hfileName)) {
564 restoreReferenceFile(familyDir, regionInfo, storeFile);
565 } else {
566 HFileLink.create(conf, fs, familyDir, regionInfo, hfileName);
567 }
568 }
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588 private void restoreReferenceFile(final Path familyDir, final HRegionInfo regionInfo,
589 final SnapshotRegionManifest.StoreFile storeFile) throws IOException {
590 String hfileName = storeFile.getName();
591
592
593 Path refPath = StoreFileInfo.getReferredToFile(new Path(new Path(new Path(
594 snapshotTable.getNameAsString(), regionInfo.getEncodedName()), familyDir.getName()),
595 hfileName));
596 String snapshotRegionName = refPath.getParent().getParent().getName();
597 String fileName = refPath.getName();
598
599
600 String clonedRegionName = Bytes.toString(regionsMap.get(Bytes.toBytes(snapshotRegionName)));
601 if (clonedRegionName == null) clonedRegionName = snapshotRegionName;
602
603
604 Path linkPath = null;
605 String refLink = fileName;
606 if (!HFileLink.isHFileLink(fileName)) {
607 refLink = HFileLink.createHFileLinkName(snapshotTable, snapshotRegionName, fileName);
608 linkPath = new Path(familyDir,
609 HFileLink.createHFileLinkName(snapshotTable, regionInfo.getEncodedName(), hfileName));
610 }
611
612 Path outPath = new Path(familyDir, refLink + '.' + clonedRegionName);
613
614
615 if (storeFile.hasReference()) {
616 Reference reference = Reference.convert(storeFile.getReference());
617 reference.write(fs, outPath);
618 } else {
619 InputStream in;
620 if (linkPath != null) {
621 in = new HFileLink(conf, linkPath).open(fs);
622 } else {
623 linkPath = new Path(new Path(HRegion.getRegionDir(snapshotManifest.getSnapshotDir(),
624 regionInfo.getEncodedName()), familyDir.getName()), hfileName);
625 in = fs.open(linkPath);
626 }
627 OutputStream out = fs.create(outPath);
628 IOUtils.copyBytes(in, out, conf);
629 }
630
631
632 String regionName = Bytes.toString(regionsMap.get(regionInfo.getEncodedNameAsBytes()));
633 LOG.debug("Restore reference " + regionName + " to " + clonedRegionName);
634 synchronized (parentsMap) {
635 Pair<String, String> daughters = parentsMap.get(clonedRegionName);
636 if (daughters == null) {
637 daughters = new Pair<String, String>(regionName, null);
638 parentsMap.put(clonedRegionName, daughters);
639 } else if (!regionName.equals(daughters.getFirst())) {
640 daughters.setSecond(regionName);
641 }
642 }
643 }
644
645
646
647
648
649
650
651
652
653 public HRegionInfo cloneRegionInfo(final HRegionInfo snapshotRegionInfo) {
654 HRegionInfo regionInfo = new HRegionInfo(tableDesc.getTableName(),
655 snapshotRegionInfo.getStartKey(), snapshotRegionInfo.getEndKey(),
656 snapshotRegionInfo.isSplit(), snapshotRegionInfo.getRegionId());
657 regionInfo.setOffline(snapshotRegionInfo.isOffline());
658 return regionInfo;
659 }
660
661
662
663
664 private List<HRegionInfo> getTableRegions() throws IOException {
665 LOG.debug("get table regions: " + tableDir);
666 FileStatus[] regionDirs = FSUtils.listStatus(fs, tableDir, new FSUtils.RegionDirFilter(fs));
667 if (regionDirs == null) return null;
668
669 List<HRegionInfo> regions = new LinkedList<HRegionInfo>();
670 for (FileStatus regionDir: regionDirs) {
671 HRegionInfo hri = HRegionFileSystem.loadRegionInfoFileContent(fs, regionDir.getPath());
672 regions.add(hri);
673 }
674 LOG.debug("found " + regions.size() + " regions for table=" +
675 tableDesc.getTableName().getNameAsString());
676 return regions;
677 }
678
679
680
681
682
683
684
685
686
687 public static HTableDescriptor cloneTableSchema(final HTableDescriptor snapshotTableDescriptor,
688 final TableName tableName) throws IOException {
689 HTableDescriptor htd = new HTableDescriptor(tableName);
690 for (HColumnDescriptor hcd: snapshotTableDescriptor.getColumnFamilies()) {
691 htd.addFamily(hcd);
692 }
693 for (Map.Entry<ImmutableBytesWritable, ImmutableBytesWritable> e:
694 snapshotTableDescriptor.getValues().entrySet()) {
695 htd.setValue(e.getKey(), e.getValue());
696 }
697 for (Map.Entry<String, String> e: snapshotTableDescriptor.getConfiguration().entrySet()) {
698 htd.setConfiguration(e.getKey(), e.getValue());
699 }
700 return htd;
701 }
702
703
704
705
706
707
708
709
710
711
712 public static void copySnapshotForScanner(Configuration conf, FileSystem fs, Path rootDir,
713 Path restoreDir, String snapshotName) throws IOException {
714
715 if (!restoreDir.getFileSystem(conf).getUri().equals(rootDir.getFileSystem(conf).getUri())) {
716 throw new IllegalArgumentException("Filesystems for restore directory and HBase root directory " +
717 "should be the same");
718 }
719 if (restoreDir.toUri().getPath().startsWith(rootDir.toUri().getPath())) {
720 throw new IllegalArgumentException("Restore directory cannot be a sub directory of HBase " +
721 "root directory. RootDir: " + rootDir + ", restoreDir: " + restoreDir);
722 }
723
724 Path snapshotDir = SnapshotDescriptionUtils.getCompletedSnapshotDir(snapshotName, rootDir);
725 SnapshotDescription snapshotDesc = SnapshotDescriptionUtils.readSnapshotInfo(fs, snapshotDir);
726 SnapshotManifest manifest = SnapshotManifest.open(conf, fs, snapshotDir, snapshotDesc);
727
728 MonitoredTask status = TaskMonitor.get().createStatus(
729 "Restoring snapshot '" + snapshotName + "' to directory " + restoreDir);
730 ForeignExceptionDispatcher monitor = new ForeignExceptionDispatcher();
731
732 RestoreSnapshotHelper helper = new RestoreSnapshotHelper(conf, fs,
733 manifest, manifest.getTableDescriptor(), restoreDir, monitor, status);
734 helper.restoreHdfsRegions();
735
736 if (LOG.isDebugEnabled()) {
737 LOG.debug("Restored table dir:" + restoreDir);
738 FSUtils.logFileSystemState(fs, restoreDir, LOG);
739 }
740 }
741 }