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.FileNotFoundException;
23 import java.net.URI;
24 import java.text.SimpleDateFormat;
25 import java.util.ArrayList;
26 import java.util.Collections;
27 import java.util.Date;
28 import java.util.List;
29 import java.util.Map;
30 import java.util.concurrent.ConcurrentHashMap;
31 import java.util.concurrent.ExecutorService;
32 import java.util.concurrent.atomic.AtomicInteger;
33 import java.util.concurrent.atomic.AtomicLong;
34
35 import org.apache.commons.logging.Log;
36 import org.apache.commons.logging.LogFactory;
37
38 import org.apache.hadoop.fs.Path;
39 import org.apache.hadoop.fs.FileStatus;
40 import org.apache.hadoop.fs.FileSystem;
41 import org.apache.hadoop.hbase.classification.InterfaceAudience;
42 import org.apache.hadoop.hbase.classification.InterfaceStability;
43 import org.apache.hadoop.conf.Configured;
44 import org.apache.hadoop.hbase.HRegionInfo;
45 import org.apache.hadoop.hbase.TableName;
46 import org.apache.hadoop.util.StringUtils;
47 import org.apache.hadoop.util.Tool;
48 import org.apache.hadoop.util.ToolRunner;
49
50 import org.apache.hadoop.conf.Configuration;
51 import org.apache.hadoop.hbase.HBaseConfiguration;
52 import org.apache.hadoop.hbase.io.HFileLink;
53 import org.apache.hadoop.hbase.io.WALLink;
54 import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.SnapshotDescription;
55 import org.apache.hadoop.hbase.protobuf.generated.SnapshotProtos.SnapshotRegionManifest;
56 import org.apache.hadoop.hbase.util.FSUtils;
57
58
59
60
61
62
63
64
65
66
67 @InterfaceAudience.Public
68 @InterfaceStability.Evolving
69 public final class SnapshotInfo extends Configured implements Tool {
70 private static final Log LOG = LogFactory.getLog(SnapshotInfo.class);
71
72
73
74
75
76
77
78
79
80 public static class SnapshotStats {
81
82 static class FileInfo {
83 private final boolean corrupted;
84 private final boolean inArchive;
85 private final long size;
86
87 FileInfo(final boolean inArchive, final long size, final boolean corrupted) {
88 this.corrupted = corrupted;
89 this.inArchive = inArchive;
90 this.size = size;
91 }
92
93
94 public boolean inArchive() {
95 return this.inArchive;
96 }
97
98
99 public boolean isCorrupted() {
100 return this.corrupted;
101 }
102
103
104 public boolean isMissing() {
105 return this.size < 0;
106 }
107
108
109 public long getSize() {
110 return this.size;
111 }
112
113 String getStateToString() {
114 if (isCorrupted()) return "CORRUPTED";
115 if (isMissing()) return "NOT FOUND";
116 if (inArchive()) return "archive";
117 return null;
118 }
119 }
120
121 private AtomicInteger hfileArchiveCount = new AtomicInteger();
122 private AtomicInteger hfilesCorrupted = new AtomicInteger();
123 private AtomicInteger hfilesMissing = new AtomicInteger();
124 private AtomicInteger hfilesCount = new AtomicInteger();
125 private AtomicInteger logsMissing = new AtomicInteger();
126 private AtomicInteger logsCount = new AtomicInteger();
127 private AtomicLong hfileArchiveSize = new AtomicLong();
128 private AtomicLong hfileSize = new AtomicLong();
129 private AtomicLong nonSharedHfilesArchiveSize = new AtomicLong();
130 private AtomicLong logSize = new AtomicLong();
131
132 private final SnapshotDescription snapshot;
133 private final TableName snapshotTable;
134 private final Configuration conf;
135 private final FileSystem fs;
136
137 SnapshotStats(final Configuration conf, final FileSystem fs, final SnapshotDescription snapshot)
138 {
139 this.snapshot = snapshot;
140 this.snapshotTable = TableName.valueOf(snapshot.getTable());
141 this.conf = conf;
142 this.fs = fs;
143 }
144
145
146 public SnapshotDescription getSnapshotDescription() {
147 return this.snapshot;
148 }
149
150
151 public boolean isSnapshotCorrupted() {
152 return hfilesMissing.get() > 0 ||
153 logsMissing.get() > 0 ||
154 hfilesCorrupted.get() > 0;
155 }
156
157
158 public int getStoreFilesCount() {
159 return hfilesCount.get() + hfileArchiveCount.get();
160 }
161
162
163 public int getArchivedStoreFilesCount() {
164 return hfileArchiveCount.get();
165 }
166
167
168 public int getLogsCount() {
169 return logsCount.get();
170 }
171
172
173 public int getMissingStoreFilesCount() {
174 return hfilesMissing.get();
175 }
176
177
178 public int getCorruptedStoreFilesCount() {
179 return hfilesCorrupted.get();
180 }
181
182
183 public int getMissingLogsCount() {
184 return logsMissing.get();
185 }
186
187
188 public long getStoreFilesSize() {
189 return hfileSize.get() + hfileArchiveSize.get();
190 }
191
192
193 public long getSharedStoreFilesSize() {
194 return hfileSize.get();
195 }
196
197
198 public long getArchivedStoreFileSize() {
199 return hfileArchiveSize.get();
200 }
201
202
203
204
205
206
207 public long getNonSharedArchivedStoreFilesSize() {
208 return nonSharedHfilesArchiveSize.get();
209 }
210
211
212 public float getSharedStoreFilePercentage() {
213 return ((float)hfileSize.get() / (hfileSize.get() + hfileArchiveSize.get())) * 100;
214 }
215
216
217 public long getLogsSize() {
218 return logSize.get();
219 }
220
221
222
223
224
225
226
227
228 private boolean isArchivedFileStillReferenced(final Path filePath,
229 final Map<Path, Integer> snapshotFilesMap) {
230
231 Integer c = snapshotFilesMap.get(filePath);
232
233
234
235 if ((c != null) && (c == 1)) {
236 Path parentDir = filePath.getParent();
237 Path backRefDir = HFileLink.getBackReferencesDir(parentDir, filePath.getName());
238 try {
239 if (FSUtils.listStatus(fs, backRefDir) == null) {
240 return false;
241 }
242 } catch (IOException e) {
243
244
245 }
246 }
247 return true;
248 }
249
250
251
252
253
254
255
256
257
258 FileInfo addStoreFile(final HRegionInfo region, final String family,
259 final SnapshotRegionManifest.StoreFile storeFile,
260 final Map<Path, Integer> filesMap) throws IOException {
261 HFileLink link = HFileLink.build(conf, snapshotTable, region.getEncodedName(),
262 family, storeFile.getName());
263 boolean isCorrupted = false;
264 boolean inArchive = false;
265 long size = -1;
266 try {
267 if ((inArchive = fs.exists(link.getArchivePath()))) {
268 size = fs.getFileStatus(link.getArchivePath()).getLen();
269 hfileArchiveSize.addAndGet(size);
270 hfileArchiveCount.incrementAndGet();
271
272
273
274 if ((filesMap != null) &&
275 !isArchivedFileStillReferenced(link.getArchivePath(), filesMap)) {
276 nonSharedHfilesArchiveSize.addAndGet(size);
277 }
278 } else {
279 size = link.getFileStatus(fs).getLen();
280 hfileSize.addAndGet(size);
281 hfilesCount.incrementAndGet();
282 }
283 isCorrupted = (storeFile.hasFileSize() && storeFile.getFileSize() != size);
284 if (isCorrupted) hfilesCorrupted.incrementAndGet();
285 } catch (FileNotFoundException e) {
286 hfilesMissing.incrementAndGet();
287 }
288 return new FileInfo(inArchive, size, isCorrupted);
289 }
290
291
292
293
294
295
296
297 FileInfo addLogFile(final String server, final String logfile) throws IOException {
298 WALLink logLink = new WALLink(conf, server, logfile);
299 long size = -1;
300 try {
301 size = logLink.getFileStatus(fs).getLen();
302 logSize.addAndGet(size);
303 logsCount.incrementAndGet();
304 } catch (FileNotFoundException e) {
305 logsMissing.incrementAndGet();
306 }
307 return new FileInfo(false, size, false);
308 }
309 }
310
311 private boolean printSizeInBytes = false;
312 private FileSystem fs;
313 private Path rootDir;
314
315 private SnapshotManifest snapshotManifest;
316
317 @Override
318 @edu.umd.cs.findbugs.annotations.SuppressWarnings(value="REC_CATCH_EXCEPTION",
319 justification="Intentional")
320 public int run(String[] args) throws IOException, InterruptedException {
321 final Configuration conf = getConf();
322 boolean listSnapshots = false;
323 String snapshotName = null;
324 boolean showSchema = false;
325 boolean showFiles = false;
326 boolean showStats = false;
327
328
329 for (int i = 0; i < args.length; i++) {
330 String cmd = args[i];
331 try {
332 if (cmd.equals("-snapshot")) {
333 snapshotName = args[++i];
334 } else if (cmd.equals("-files")) {
335 showFiles = true;
336 showStats = true;
337 } else if (cmd.equals("-stats")) {
338 showStats = true;
339 } else if (cmd.equals("-schema")) {
340 showSchema = true;
341 } else if (cmd.equals("-remote-dir")) {
342 Path sourceDir = new Path(args[++i]);
343 URI defaultFs = sourceDir.getFileSystem(conf).getUri();
344 FSUtils.setFsDefault(conf, new Path(defaultFs));
345 FSUtils.setRootDir(conf, sourceDir);
346 } else if (cmd.equals("-list-snapshots")) {
347 listSnapshots = true;
348 } else if (cmd.equals("-size-in-bytes")) {
349 printSizeInBytes = true;
350 } else if (cmd.equals("-h") || cmd.equals("--help")) {
351 printUsageAndExit();
352 } else {
353 System.err.println("UNEXPECTED: " + cmd);
354 printUsageAndExit();
355 }
356 } catch (Exception e) {
357 printUsageAndExit();
358 }
359 }
360
361
362 if (listSnapshots) {
363 SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
364 System.out.printf("%-20s | %-20s | %s%n", "SNAPSHOT", "CREATION TIME", "TABLE NAME");
365 for (SnapshotDescription desc: getSnapshotList(conf)) {
366 System.out.printf("%-20s | %20s | %s%n",
367 desc.getName(),
368 df.format(new Date(desc.getCreationTime())),
369 desc.getTable());
370 }
371 return 0;
372 }
373
374 if (snapshotName == null) {
375 System.err.println("Missing snapshot name!");
376 printUsageAndExit();
377 return 1;
378 }
379
380 rootDir = FSUtils.getRootDir(conf);
381 fs = FileSystem.get(rootDir.toUri(), conf);
382 LOG.debug("fs=" + fs.getUri().toString() + " root=" + rootDir);
383
384
385 if (!loadSnapshotInfo(snapshotName)) {
386 System.err.println("Snapshot '" + snapshotName + "' not found!");
387 return 1;
388 }
389
390 printInfo();
391 if (showSchema) printSchema();
392 printFiles(showFiles, showStats);
393
394 return 0;
395 }
396
397
398
399
400
401
402 private boolean loadSnapshotInfo(final String snapshotName) throws IOException {
403 Path snapshotDir = SnapshotDescriptionUtils.getCompletedSnapshotDir(snapshotName, rootDir);
404 if (!fs.exists(snapshotDir)) {
405 LOG.warn("Snapshot '" + snapshotName + "' not found in: " + snapshotDir);
406 return false;
407 }
408
409 SnapshotDescription snapshotDesc = SnapshotDescriptionUtils.readSnapshotInfo(fs, snapshotDir);
410 snapshotManifest = SnapshotManifest.open(getConf(), fs, snapshotDir, snapshotDesc);
411 return true;
412 }
413
414
415
416
417 private void printInfo() {
418 SnapshotDescription snapshotDesc = snapshotManifest.getSnapshotDescription();
419 SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
420 System.out.println("Snapshot Info");
421 System.out.println("----------------------------------------");
422 System.out.println(" Name: " + snapshotDesc.getName());
423 System.out.println(" Type: " + snapshotDesc.getType());
424 System.out.println(" Table: " + snapshotDesc.getTable());
425 System.out.println(" Format: " + snapshotDesc.getVersion());
426 System.out.println("Created: " + df.format(new Date(snapshotDesc.getCreationTime())));
427 System.out.println();
428 }
429
430
431
432
433 private void printSchema() {
434 System.out.println("Table Descriptor");
435 System.out.println("----------------------------------------");
436 System.out.println(snapshotManifest.getTableDescriptor().toString());
437 System.out.println();
438 }
439
440
441
442
443
444 private void printFiles(final boolean showFiles, final boolean showStats) throws IOException {
445 if (showFiles) {
446 System.out.println("Snapshot Files");
447 System.out.println("----------------------------------------");
448 }
449
450
451 final SnapshotDescription snapshotDesc = snapshotManifest.getSnapshotDescription();
452 final String table = snapshotDesc.getTable();
453 final SnapshotStats stats = new SnapshotStats(this.getConf(), this.fs, snapshotDesc);
454 SnapshotReferenceUtil.concurrentVisitReferencedFiles(getConf(), fs, snapshotManifest,
455 "SnapshotInfo", new SnapshotReferenceUtil.SnapshotVisitor() {
456 @Override public void storeFile(final HRegionInfo regionInfo, final String family,
457 final SnapshotRegionManifest.StoreFile storeFile) throws IOException {
458 if (storeFile.hasReference()) return;
459
460 SnapshotStats.FileInfo info = stats.addStoreFile(regionInfo, family, storeFile, null);
461 if (showFiles) {
462 String state = info.getStateToString();
463 System.out.printf("%8s %s/%s/%s/%s %s%n",
464 (info.isMissing() ? "-" : fileSizeToString(info.getSize())), table,
465 regionInfo.getEncodedName(), family, storeFile.getName(),
466 state == null ? "" : "(" + state + ")");
467 }
468 }
469 });
470
471
472 System.out.println();
473 if (stats.isSnapshotCorrupted()) {
474 System.out.println("**************************************************************");
475 System.out.printf("BAD SNAPSHOT: %d hfile(s) and %d log(s) missing.%n",
476 stats.getMissingStoreFilesCount(), stats.getMissingLogsCount());
477 System.out.printf(" %d hfile(s) corrupted.%n",
478 stats.getCorruptedStoreFilesCount());
479 System.out.println("**************************************************************");
480 }
481
482 if (showStats) {
483 System.out.printf("%d HFiles (%d in archive), total size %s (%.2f%% %s shared with the source table)%n",
484 stats.getStoreFilesCount(), stats.getArchivedStoreFilesCount(),
485 fileSizeToString(stats.getStoreFilesSize()),
486 stats.getSharedStoreFilePercentage(),
487 fileSizeToString(stats.getSharedStoreFilesSize())
488 );
489 System.out.printf("%d Logs, total size %s%n",
490 stats.getLogsCount(), fileSizeToString(stats.getLogsSize()));
491 System.out.println();
492 }
493 }
494
495 private String fileSizeToString(long size) {
496 return printSizeInBytes ? Long.toString(size) : StringUtils.humanReadableInt(size);
497 }
498
499 private void printUsageAndExit() {
500 System.err.printf("Usage: bin/hbase snapshot info [options]%n");
501 System.err.println(" where [options] are:");
502 System.err.println(" -h|-help Show this help and exit.");
503 System.err.println(" -remote-dir Root directory that contains the snapshots.");
504 System.err.println(" -list-snapshots List all the available snapshots and exit.");
505 System.err.println(" -size-in-bytes Print the size of the files in bytes.");
506 System.err.println(" -snapshot NAME Snapshot to examine.");
507 System.err.println(" -files Files and logs list.");
508 System.err.println(" -stats Files and logs stats.");
509 System.err.println(" -schema Describe the snapshotted table.");
510 System.err.println();
511 System.err.println("Examples:");
512 System.err.println(" hbase snapshot info \\");
513 System.err.println(" -snapshot MySnapshot -files");
514 System.exit(1);
515 }
516
517
518
519
520
521
522
523 public static SnapshotStats getSnapshotStats(final Configuration conf,
524 final SnapshotDescription snapshot) throws IOException {
525 return getSnapshotStats(conf, snapshot, null);
526 }
527
528
529
530
531
532
533
534
535 public static SnapshotStats getSnapshotStats(final Configuration conf,
536 final SnapshotDescription snapshot,
537 final Map<Path, Integer> filesMap) throws IOException {
538 Path rootDir = FSUtils.getRootDir(conf);
539 FileSystem fs = FileSystem.get(rootDir.toUri(), conf);
540 Path snapshotDir = SnapshotDescriptionUtils.getCompletedSnapshotDir(snapshot, rootDir);
541 SnapshotManifest manifest = SnapshotManifest.open(conf, fs, snapshotDir, snapshot);
542 final SnapshotStats stats = new SnapshotStats(conf, fs, snapshot);
543 SnapshotReferenceUtil.concurrentVisitReferencedFiles(conf, fs, manifest,
544 "SnapshotsStatsAggregation", new SnapshotReferenceUtil.SnapshotVisitor() {
545 @Override
546 public void storeFile(final HRegionInfo regionInfo, final String family,
547 final SnapshotRegionManifest.StoreFile storeFile) throws IOException {
548 if (!storeFile.hasReference()) {
549 stats.addStoreFile(regionInfo, family, storeFile, filesMap);
550 }
551 }});
552 return stats;
553 }
554
555
556
557
558
559
560 public static List<SnapshotDescription> getSnapshotList(final Configuration conf)
561 throws IOException {
562 Path rootDir = FSUtils.getRootDir(conf);
563 FileSystem fs = FileSystem.get(rootDir.toUri(), conf);
564 Path snapshotDir = SnapshotDescriptionUtils.getSnapshotsDir(rootDir);
565 FileStatus[] snapshots = fs.listStatus(snapshotDir,
566 new SnapshotDescriptionUtils.CompletedSnaphotDirectoriesFilter(fs));
567 List<SnapshotDescription> snapshotLists =
568 new ArrayList<SnapshotDescription>(snapshots.length);
569 for (FileStatus snapshotDirStat: snapshots) {
570 snapshotLists.add(SnapshotDescriptionUtils.readSnapshotInfo(fs, snapshotDirStat.getPath()));
571 }
572 return snapshotLists;
573 }
574
575
576
577
578
579
580
581
582
583
584
585 private static void getSnapshotFilesMap(final Configuration conf,
586 final SnapshotDescription snapshot, final ExecutorService exec,
587 final ConcurrentHashMap<Path, Integer> filesMap,
588 final AtomicLong uniqueHFilesArchiveSize, final AtomicLong uniqueHFilesSize)
589 throws IOException {
590 Path rootDir = FSUtils.getRootDir(conf);
591 final FileSystem fs = FileSystem.get(rootDir.toUri(), conf);
592
593 Path snapshotDir = SnapshotDescriptionUtils.getCompletedSnapshotDir(snapshot, rootDir);
594 SnapshotManifest manifest = SnapshotManifest.open(conf, fs, snapshotDir, snapshot);
595 SnapshotReferenceUtil.concurrentVisitReferencedFiles(conf, fs, manifest, exec,
596 new SnapshotReferenceUtil.SnapshotVisitor() {
597 @Override public void storeFile(final HRegionInfo regionInfo, final String family,
598 final SnapshotRegionManifest.StoreFile storeFile) throws IOException {
599 if (!storeFile.hasReference()) {
600 HFileLink link = HFileLink
601 .build(conf, TableName.valueOf(snapshot.getTable()), regionInfo.getEncodedName(),
602 family, storeFile.getName());
603 long size;
604 Integer count;
605 Path p;
606 AtomicLong al;
607 int c = 0;
608
609 if (fs.exists(link.getArchivePath())) {
610 p = link.getArchivePath();
611 al = uniqueHFilesArchiveSize;
612 size = fs.getFileStatus(p).getLen();
613 } else {
614 p = link.getOriginPath();
615 al = uniqueHFilesSize;
616 size = link.getFileStatus(fs).getLen();
617 }
618
619
620 count = filesMap.get(p);
621 if (count != null) {
622 c = count.intValue();
623 } else {
624 al.addAndGet(size);
625 }
626
627 filesMap.put(p, ++c);
628 }
629 }
630 });
631 }
632
633
634
635
636
637
638
639
640 public static Map<Path, Integer> getSnapshotsFilesMap(final Configuration conf,
641 AtomicLong uniqueHFilesArchiveSize, AtomicLong uniqueHFilesSize) throws IOException {
642 List<SnapshotDescription> snapshotList = getSnapshotList(conf);
643
644 if (snapshotList.size() == 0) {
645 return Collections.emptyMap();
646 }
647
648 ConcurrentHashMap<Path, Integer> fileMap = new ConcurrentHashMap<>();
649
650 ExecutorService exec = SnapshotManifest.createExecutor(conf, "SnapshotsFilesMapping");
651
652 try {
653 for (final SnapshotDescription snapshot : snapshotList) {
654 getSnapshotFilesMap(conf, snapshot, exec, fileMap, uniqueHFilesArchiveSize,
655 uniqueHFilesSize);
656 }
657 } finally {
658 exec.shutdown();
659 }
660
661 return fileMap;
662 }
663
664
665
666
667
668
669
670
671 static int innerMain(final String [] args) throws Exception {
672 return ToolRunner.run(HBaseConfiguration.create(), new SnapshotInfo(), args);
673 }
674
675 public static void main(String[] args) throws Exception {
676 System.exit(innerMain(args));
677 }
678 }