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