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