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