1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.hadoop.hbase.backup;
19
20 import java.io.FileNotFoundException;
21 import java.io.IOException;
22 import java.util.ArrayList;
23 import java.util.Arrays;
24 import java.util.Collection;
25 import java.util.Collections;
26 import java.util.List;
27
28 import org.apache.commons.logging.Log;
29 import org.apache.commons.logging.LogFactory;
30 import org.apache.hadoop.conf.Configuration;
31 import org.apache.hadoop.fs.FileStatus;
32 import org.apache.hadoop.fs.FileSystem;
33 import org.apache.hadoop.fs.Path;
34 import org.apache.hadoop.fs.PathFilter;
35 import org.apache.hadoop.hbase.HBaseInterfaceAudience;
36 import org.apache.hadoop.hbase.HRegionInfo;
37 import org.apache.hadoop.hbase.classification.InterfaceAudience;
38 import org.apache.hadoop.hbase.regionserver.HRegion;
39 import org.apache.hadoop.hbase.regionserver.StoreFile;
40 import org.apache.hadoop.hbase.util.Bytes;
41 import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
42 import org.apache.hadoop.hbase.util.FSUtils;
43 import org.apache.hadoop.hbase.util.HFileArchiveUtil;
44 import org.apache.hadoop.io.MultipleIOException;
45
46 import com.google.common.base.Function;
47 import com.google.common.base.Preconditions;
48 import com.google.common.collect.Collections2;
49 import com.google.common.collect.Lists;
50
51
52
53
54
55
56 @InterfaceAudience.Private
57 public class HFileArchiver {
58 private static final Log LOG = LogFactory.getLog(HFileArchiver.class);
59 private static final String SEPARATOR = ".";
60
61
62 private static final int DEFAULT_RETRIES_NUMBER = 3;
63
64 private static final Function<File, Path> FUNC_FILE_TO_PATH =
65 new Function<File, Path>() {
66 @Override
67 public Path apply(File file) {
68 return file == null ? null : file.getPath();
69 }
70 };
71
72 private HFileArchiver() {
73
74 }
75
76
77
78
79
80
81
82
83
84 public static void archiveRegion(Configuration conf, FileSystem fs, HRegionInfo info)
85 throws IOException {
86 Path rootDir = FSUtils.getRootDir(conf);
87 archiveRegion(fs, rootDir, FSUtils.getTableDir(rootDir, info.getTable()),
88 HRegion.getRegionDir(rootDir, info));
89 }
90
91
92
93
94
95
96
97
98
99
100
101
102 public static boolean archiveRegion(FileSystem fs, Path rootdir, Path tableDir, Path regionDir)
103 throws IOException {
104 if (LOG.isDebugEnabled()) {
105 LOG.debug("ARCHIVING " + regionDir.toString());
106 }
107
108
109
110 if (tableDir == null || regionDir == null) {
111 LOG.error("No archive directory could be found because tabledir (" + tableDir
112 + ") or regiondir (" + regionDir + "was null. Deleting files instead.");
113 deleteRegionWithoutArchiving(fs, regionDir);
114
115
116 return false;
117 }
118
119
120 Preconditions.checkArgument(regionDir.toString().startsWith(tableDir.toString()));
121 Path regionArchiveDir = HFileArchiveUtil.getRegionArchiveDir(rootdir,
122 FSUtils.getTableName(tableDir),
123 regionDir.getName());
124
125 FileStatusConverter getAsFile = new FileStatusConverter(fs);
126
127
128
129 Collection<File> toArchive = new ArrayList<File>();
130 final PathFilter dirFilter = new FSUtils.DirFilter(fs);
131 PathFilter nonHidden = new PathFilter() {
132 @Override
133 public boolean accept(Path file) {
134 return dirFilter.accept(file) && !file.getName().toString().startsWith(".");
135 }
136 };
137 FileStatus[] storeDirs = FSUtils.listStatus(fs, regionDir, nonHidden);
138
139 if (storeDirs == null) {
140 LOG.debug("Region directory (" + regionDir + ") was empty, just deleting and returning!");
141 return deleteRegionWithoutArchiving(fs, regionDir);
142 }
143
144
145 toArchive.addAll(Lists.transform(Arrays.asList(storeDirs), getAsFile));
146 LOG.debug("Archiving " + toArchive);
147 List<File> failedArchive = resolveAndArchive(fs, regionArchiveDir, toArchive,
148 EnvironmentEdgeManager.currentTime());
149 if (!failedArchive.isEmpty()) {
150 throw new FailedArchiveException("Failed to archive/delete all the files for region:"
151 + regionDir.getName() + " into " + regionArchiveDir
152 + ". Something is probably awry on the filesystem.",
153 Collections2.transform(failedArchive, FUNC_FILE_TO_PATH));
154 }
155
156 return deleteRegionWithoutArchiving(fs, regionDir);
157 }
158
159
160
161
162
163
164
165
166
167
168
169 public static void archiveFamily(FileSystem fs, Configuration conf,
170 HRegionInfo parent, Path tableDir, byte[] family) throws IOException {
171 Path familyDir = new Path(tableDir, new Path(parent.getEncodedName(), Bytes.toString(family)));
172 FileStatus[] storeFiles = FSUtils.listStatus(fs, familyDir);
173 if (storeFiles == null) {
174 LOG.debug("No store files to dispose for region=" + parent.getRegionNameAsString() +
175 ", family=" + Bytes.toString(family));
176 return;
177 }
178
179 FileStatusConverter getAsFile = new FileStatusConverter(fs);
180 Collection<File> toArchive = Lists.transform(Arrays.asList(storeFiles), getAsFile);
181 Path storeArchiveDir = HFileArchiveUtil.getStoreArchivePath(conf, parent, tableDir, family);
182
183
184 List<File> failedArchive = resolveAndArchive(fs, storeArchiveDir, toArchive,
185 EnvironmentEdgeManager.currentTime());
186 if (!failedArchive.isEmpty()){
187 throw new FailedArchiveException("Failed to archive/delete all the files for region:"
188 + Bytes.toString(parent.getRegionName()) + ", family:" + Bytes.toString(family)
189 + " into " + storeArchiveDir + ". Something is probably awry on the filesystem.",
190 Collections2.transform(failedArchive, FUNC_FILE_TO_PATH));
191 }
192 }
193
194
195
196
197
198
199
200
201
202
203
204 public static void archiveStoreFiles(Configuration conf, FileSystem fs, HRegionInfo regionInfo,
205 Path tableDir, byte[] family, Collection<StoreFile> compactedFiles)
206 throws IOException, FailedArchiveException {
207
208
209 if (fs == null) {
210 LOG.warn("Passed filesystem is null, so just deleting the files without archiving for region:"
211 + Bytes.toString(regionInfo.getRegionName()) + ", family:" + Bytes.toString(family));
212 deleteStoreFilesWithoutArchiving(compactedFiles);
213 return;
214 }
215
216
217 if (compactedFiles.size() == 0) {
218 LOG.debug("No store files to dispose, done!");
219 return;
220 }
221
222
223 if (regionInfo == null || family == null) throw new IOException(
224 "Need to have a region and a family to archive from.");
225
226 Path storeArchiveDir = HFileArchiveUtil.getStoreArchivePath(conf, regionInfo, tableDir, family);
227
228
229 if (!fs.mkdirs(storeArchiveDir)) {
230 throw new IOException("Could not make archive directory (" + storeArchiveDir + ") for store:"
231 + Bytes.toString(family) + ", deleting compacted files instead.");
232 }
233
234
235 if (LOG.isDebugEnabled()) LOG.debug("Archiving compacted store files.");
236
237
238 StoreToFile getStorePath = new StoreToFile(fs);
239 Collection<File> storeFiles = Collections2.transform(compactedFiles, getStorePath);
240
241
242 List<File> failedArchive = resolveAndArchive(fs, storeArchiveDir, storeFiles,
243 EnvironmentEdgeManager.currentTime());
244
245 if (!failedArchive.isEmpty()){
246 throw new FailedArchiveException("Failed to archive/delete all the files for region:"
247 + Bytes.toString(regionInfo.getRegionName()) + ", family:" + Bytes.toString(family)
248 + " into " + storeArchiveDir + ". Something is probably awry on the filesystem.",
249 Collections2.transform(failedArchive, FUNC_FILE_TO_PATH));
250 }
251 }
252
253
254
255
256
257
258
259
260
261
262
263 public static void archiveStoreFile(Configuration conf, FileSystem fs, HRegionInfo regionInfo,
264 Path tableDir, byte[] family, Path storeFile) throws IOException {
265 Path storeArchiveDir = HFileArchiveUtil.getStoreArchivePath(conf, regionInfo, tableDir, family);
266
267 if (!fs.mkdirs(storeArchiveDir)) {
268 throw new IOException("Could not make archive directory (" + storeArchiveDir + ") for store:"
269 + Bytes.toString(family) + ", deleting compacted files instead.");
270 }
271
272
273 long start = EnvironmentEdgeManager.currentTime();
274 File file = new FileablePath(fs, storeFile);
275 if (!resolveAndArchiveFile(storeArchiveDir, file, Long.toString(start))) {
276 throw new IOException("Failed to archive/delete the file for region:"
277 + regionInfo.getRegionNameAsString() + ", family:" + Bytes.toString(family)
278 + " into " + storeArchiveDir + ". Something is probably awry on the filesystem.");
279 }
280 }
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296 private static List<File> resolveAndArchive(FileSystem fs, Path baseArchiveDir,
297 Collection<File> toArchive, long start) throws IOException {
298
299 if (toArchive.size() == 0) return Collections.emptyList();
300
301 if (LOG.isTraceEnabled()) LOG.trace("moving files to the archive directory: " + baseArchiveDir);
302
303
304 if (!fs.exists(baseArchiveDir)) {
305 if (!fs.mkdirs(baseArchiveDir)) {
306 throw new IOException("Failed to create the archive directory:" + baseArchiveDir
307 + ", quitting archive attempt.");
308 }
309 if (LOG.isTraceEnabled()) LOG.trace("Created archive directory:" + baseArchiveDir);
310 }
311
312 List<File> failures = new ArrayList<File>();
313 String startTime = Long.toString(start);
314 for (File file : toArchive) {
315
316 try {
317 if (LOG.isTraceEnabled()) LOG.trace("Archiving: " + file);
318 if (file.isFile()) {
319
320 if (!resolveAndArchiveFile(baseArchiveDir, file, startTime)) {
321 LOG.warn("Couldn't archive " + file + " into backup directory: " + baseArchiveDir);
322 failures.add(file);
323 }
324 } else {
325
326 if (LOG.isTraceEnabled()) LOG.trace(file + " is a directory, archiving children files");
327
328 Path parentArchiveDir = new Path(baseArchiveDir, file.getName());
329
330
331 Collection<File> children = file.getChildren();
332 failures.addAll(resolveAndArchive(fs, parentArchiveDir, children, start));
333 }
334 } catch (IOException e) {
335 LOG.warn("Failed to archive " + file, e);
336 failures.add(file);
337 }
338 }
339 return failures;
340 }
341
342
343
344
345
346
347
348
349
350
351
352
353
354 private static boolean resolveAndArchiveFile(Path archiveDir, File currentFile,
355 String archiveStartTime) throws IOException {
356
357 String filename = currentFile.getName();
358 Path archiveFile = new Path(archiveDir, filename);
359 FileSystem fs = currentFile.getFileSystem();
360
361
362
363
364 if (fs.exists(archiveFile)) {
365 if (LOG.isDebugEnabled()) {
366 LOG.debug("File:" + archiveFile + " already exists in archive, moving to "
367 + "timestamped backup and overwriting current.");
368 }
369
370
371 Path backedupArchiveFile = new Path(archiveDir, filename + SEPARATOR + archiveStartTime);
372 if (!fs.rename(archiveFile, backedupArchiveFile)) {
373 LOG.error("Could not rename archive file to backup: " + backedupArchiveFile
374 + ", deleting existing file in favor of newer.");
375
376 if (!fs.delete(archiveFile, false)) {
377 throw new IOException("Couldn't delete existing archive file (" + archiveFile
378 + ") or rename it to the backup file (" + backedupArchiveFile
379 + ") to make room for similarly named file.");
380 }
381 }
382 LOG.debug("Backed up archive file from " + archiveFile);
383 }
384
385 if (LOG.isTraceEnabled()) {
386 LOG.trace("No existing file in archive for: " + archiveFile +
387 ", free to archive original file.");
388 }
389
390
391 boolean success = false;
392 for (int i = 0; !success && i < DEFAULT_RETRIES_NUMBER; ++i) {
393 if (i > 0) {
394
395
396
397
398 try {
399 if (!fs.exists(archiveDir)) {
400 if (fs.mkdirs(archiveDir)) {
401 LOG.debug("Created archive directory:" + archiveDir);
402 }
403 }
404 } catch (IOException e) {
405 LOG.warn("Failed to create directory: " + archiveDir, e);
406 }
407 }
408
409 try {
410 success = currentFile.moveAndClose(archiveFile);
411 } catch (FileNotFoundException fnfe) {
412 LOG.warn("Failed to archive " + currentFile +
413 " because it does not exist! Skipping and continuing on.", fnfe);
414 success = true;
415 } catch (IOException e) {
416 LOG.warn("Failed to archive " + currentFile + " on try #" + i, e);
417 success = false;
418 }
419 }
420
421 if (!success) {
422 LOG.error("Failed to archive " + currentFile);
423 return false;
424 }
425
426 if (LOG.isDebugEnabled()) {
427 LOG.debug("Finished archiving from " + currentFile + ", to " + archiveFile);
428 }
429 return true;
430 }
431
432
433
434
435
436
437
438
439 private static boolean deleteRegionWithoutArchiving(FileSystem fs, Path regionDir)
440 throws IOException {
441 if (fs.delete(regionDir, true)) {
442 LOG.debug("Deleted all region files in: " + regionDir);
443 return true;
444 }
445 LOG.debug("Failed to delete region directory:" + regionDir);
446 return false;
447 }
448
449
450
451
452
453
454
455
456
457
458
459
460 private static void deleteStoreFilesWithoutArchiving(Collection<StoreFile> compactedFiles)
461 throws IOException {
462 LOG.debug("Deleting store files without archiving.");
463 List<IOException> errors = new ArrayList<IOException>(0);
464 for (StoreFile hsf : compactedFiles) {
465 try {
466 hsf.deleteReader();
467 } catch (IOException e) {
468 LOG.error("Failed to delete store file:" + hsf.getPath());
469 errors.add(e);
470 }
471 }
472 if (errors.size() > 0) {
473 throw MultipleIOException.createIOException(errors);
474 }
475 }
476
477
478
479
480
481
482 private static abstract class FileConverter<T> implements Function<T, File> {
483 protected final FileSystem fs;
484
485 public FileConverter(FileSystem fs) {
486 this.fs = fs;
487 }
488 }
489
490
491
492
493 private static class FileStatusConverter extends FileConverter<FileStatus> {
494 public FileStatusConverter(FileSystem fs) {
495 super(fs);
496 }
497
498 @Override
499 public File apply(FileStatus input) {
500 return new FileablePath(fs, input.getPath());
501 }
502 }
503
504
505
506
507
508 private static class StoreToFile extends FileConverter<StoreFile> {
509 public StoreToFile(FileSystem fs) {
510 super(fs);
511 }
512
513 @Override
514 public File apply(StoreFile input) {
515 return new FileableStoreFile(fs, input);
516 }
517 }
518
519
520
521
522 private static abstract class File {
523 protected final FileSystem fs;
524
525 public File(FileSystem fs) {
526 this.fs = fs;
527 }
528
529
530
531
532
533 abstract void delete() throws IOException;
534
535
536
537
538
539
540 abstract boolean isFile() throws IOException;
541
542
543
544
545
546
547 abstract Collection<File> getChildren() throws IOException;
548
549
550
551
552
553 abstract void close() throws IOException;
554
555
556
557
558
559 abstract String getName();
560
561
562
563
564 abstract Path getPath();
565
566
567
568
569
570
571
572 public boolean moveAndClose(Path dest) throws IOException {
573 this.close();
574 Path p = this.getPath();
575 return FSUtils.renameAndSetModifyTime(fs, p, dest);
576 }
577
578
579
580
581 public FileSystem getFileSystem() {
582 return this.fs;
583 }
584
585 @Override
586 public String toString() {
587 return this.getClass() + ", file:" + getPath().toString();
588 }
589 }
590
591
592
593
594 private static class FileablePath extends File {
595 private final Path file;
596 private final FileStatusConverter getAsFile;
597
598 public FileablePath(FileSystem fs, Path file) {
599 super(fs);
600 this.file = file;
601 this.getAsFile = new FileStatusConverter(fs);
602 }
603
604 @Override
605 public void delete() throws IOException {
606 if (!fs.delete(file, true)) throw new IOException("Failed to delete:" + this.file);
607 }
608
609 @Override
610 public String getName() {
611 return file.getName();
612 }
613
614 @Override
615 public Collection<File> getChildren() throws IOException {
616 if (fs.isFile(file)) return Collections.emptyList();
617 return Collections2.transform(Arrays.asList(fs.listStatus(file)), getAsFile);
618 }
619
620 @Override
621 public boolean isFile() throws IOException {
622 return fs.isFile(file);
623 }
624
625 @Override
626 public void close() throws IOException {
627
628 }
629
630 @Override
631 Path getPath() {
632 return file;
633 }
634 }
635
636
637
638
639
640 private static class FileableStoreFile extends File {
641 StoreFile file;
642
643 public FileableStoreFile(FileSystem fs, StoreFile store) {
644 super(fs);
645 this.file = store;
646 }
647
648 @Override
649 public void delete() throws IOException {
650 file.deleteReader();
651 }
652
653 @Override
654 public String getName() {
655 return file.getPath().getName();
656 }
657
658 @Override
659 public boolean isFile() {
660 return true;
661 }
662
663 @Override
664 public Collection<File> getChildren() throws IOException {
665
666 return Collections.emptyList();
667 }
668
669 @Override
670 public void close() throws IOException {
671 file.closeReader(true);
672 }
673
674 @Override
675 Path getPath() {
676 return file.getPath();
677 }
678 }
679 }