001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.hadoop.hbase.snapshot; 019 020import static org.apache.hadoop.hbase.HConstants.DEFAULT_SNAPSHOT_TTL; 021 022import java.io.FileNotFoundException; 023import java.io.IOException; 024import java.net.URI; 025import java.text.SimpleDateFormat; 026import java.util.ArrayList; 027import java.util.Collections; 028import java.util.Date; 029import java.util.List; 030import java.util.Map; 031import java.util.Properties; 032import java.util.concurrent.ConcurrentHashMap; 033import java.util.concurrent.ExecutorService; 034import java.util.concurrent.atomic.AtomicInteger; 035import java.util.concurrent.atomic.AtomicLong; 036import org.apache.hadoop.conf.Configuration; 037import org.apache.hadoop.fs.FileStatus; 038import org.apache.hadoop.fs.FileSystem; 039import org.apache.hadoop.fs.Path; 040import org.apache.hadoop.hbase.TableName; 041import org.apache.hadoop.hbase.client.RegionInfo; 042import org.apache.hadoop.hbase.client.SnapshotDescription; 043import org.apache.hadoop.hbase.io.HFileLink; 044import org.apache.hadoop.hbase.io.WALLink; 045import org.apache.hadoop.hbase.util.AbstractHBaseTool; 046import org.apache.hadoop.hbase.util.CommonFSUtils; 047import org.apache.hadoop.hbase.util.Strings; 048import org.apache.yetus.audience.InterfaceAudience; 049import org.slf4j.Logger; 050import org.slf4j.LoggerFactory; 051 052import org.apache.hbase.thirdparty.org.apache.commons.cli.CommandLine; 053import org.apache.hbase.thirdparty.org.apache.commons.cli.CommandLineParser; 054import org.apache.hbase.thirdparty.org.apache.commons.cli.DefaultParser; 055import org.apache.hbase.thirdparty.org.apache.commons.cli.Option; 056import org.apache.hbase.thirdparty.org.apache.commons.cli.Options; 057import org.apache.hbase.thirdparty.org.apache.commons.cli.ParseException; 058 059import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil; 060import org.apache.hadoop.hbase.shaded.protobuf.generated.SnapshotProtos; 061import org.apache.hadoop.hbase.shaded.protobuf.generated.SnapshotProtos.SnapshotRegionManifest; 062 063/** 064 * Tool for dumping snapshot information. 065 * <ol> 066 * <li>Table Descriptor 067 * <li>Snapshot creation time, type, format version, ... 068 * <li>List of hfiles and wals 069 * <li>Stats about hfiles and logs sizes, percentage of shared with the source table, ... 070 * </ol> 071 */ 072@InterfaceAudience.Public 073public final class SnapshotInfo extends AbstractHBaseTool { 074 private static final Logger LOG = LoggerFactory.getLogger(SnapshotInfo.class); 075 076 private static final String EXPIRED_LABEL = "Yes"; 077 private static final String NOT_EXPIRED_LABEL = "No"; 078 private static final String TTL_FOREVER = "FOREVER"; 079 080 static final class Options { 081 static final Option SNAPSHOT = 082 new Option(null, "snapshot", true, "The name of the snapshot to be detailed."); 083 static final Option REMOTE_DIR = 084 new Option(null, "remote-dir", true, "A custom root directory where snapshots are stored. " 085 + "Use it together with the --snapshot option."); 086 static final Option LIST_SNAPSHOTS = 087 new Option(null, "list-snapshots", false, "List all the available snapshots and exit."); 088 static final Option FILES = 089 new Option(null, "files", false, "The list of files retained by the specified snapshot. " 090 + "Use it together with the --snapshot option."); 091 static final Option STATS = 092 new Option(null, "stats", false, "Additional information about the specified snapshot. " 093 + "Use it together with the --snapshot option."); 094 static final Option SCHEMA = new Option(null, "schema", false, 095 "Show the descriptor of the table for the specified snapshot. " 096 + "Use it together with the --snapshot option."); 097 static final Option SIZE_IN_BYTES = 098 new Option(null, "size-in-bytes", false, "Print the size of the files in bytes. " 099 + "Use it together with the --snapshot and --files options."); 100 } 101 102 /** 103 * Statistics about the snapshot 104 * <ol> 105 * <li>How many store files and logs are in the archive 106 * <li>How many store files and logs are shared with the table 107 * <li>Total store files and logs size and shared amount 108 * </ol> 109 */ 110 public static class SnapshotStats { 111 /** Information about the file referenced by the snapshot */ 112 static class FileInfo { 113 private final boolean corrupted; 114 private final boolean inArchive; 115 private final long size; 116 117 FileInfo(final boolean inArchive, final long size, final boolean corrupted) { 118 this.corrupted = corrupted; 119 this.inArchive = inArchive; 120 this.size = size; 121 } 122 123 /** Returns true if the file is in the archive */ 124 public boolean inArchive() { 125 return this.inArchive; 126 } 127 128 /** Returns true if the file is corrupted */ 129 public boolean isCorrupted() { 130 return this.corrupted; 131 } 132 133 /** Returns true if the file is missing */ 134 public boolean isMissing() { 135 return this.size < 0; 136 } 137 138 /** Returns the file size */ 139 public long getSize() { 140 return this.size; 141 } 142 143 String getStateToString() { 144 if (isCorrupted()) return "CORRUPTED"; 145 if (isMissing()) return "NOT FOUND"; 146 if (inArchive()) return "archive"; 147 return null; 148 } 149 } 150 151 private AtomicInteger hfilesArchiveCount = new AtomicInteger(); 152 private AtomicInteger hfilesCorrupted = new AtomicInteger(); 153 private AtomicInteger hfilesMissing = new AtomicInteger(); 154 private AtomicInteger hfilesCount = new AtomicInteger(); 155 private AtomicInteger hfilesMobCount = new AtomicInteger(); 156 private AtomicInteger logsMissing = new AtomicInteger(); 157 private AtomicInteger logsCount = new AtomicInteger(); 158 private AtomicLong hfilesArchiveSize = new AtomicLong(); 159 private AtomicLong hfilesSize = new AtomicLong(); 160 private AtomicLong hfilesMobSize = new AtomicLong(); 161 private AtomicLong nonSharedHfilesArchiveSize = new AtomicLong(); 162 private AtomicLong logSize = new AtomicLong(); 163 164 private final SnapshotProtos.SnapshotDescription snapshot; 165 private final TableName snapshotTable; 166 private final Configuration conf; 167 private final FileSystem fs; 168 169 SnapshotStats(final Configuration conf, final FileSystem fs, 170 final SnapshotDescription snapshot) { 171 this.snapshot = ProtobufUtil.createHBaseProtosSnapshotDesc(snapshot); 172 this.snapshotTable = snapshot.getTableName(); 173 this.conf = conf; 174 this.fs = fs; 175 } 176 177 SnapshotStats(final Configuration conf, final FileSystem fs, 178 final SnapshotProtos.SnapshotDescription snapshot) { 179 this.snapshot = snapshot; 180 this.snapshotTable = TableName.valueOf(snapshot.getTable()); 181 this.conf = conf; 182 this.fs = fs; 183 } 184 185 /** Returns the snapshot descriptor */ 186 public SnapshotDescription getSnapshotDescription() { 187 return ProtobufUtil.createSnapshotDesc(this.snapshot); 188 } 189 190 /** Returns true if the snapshot is corrupted */ 191 public boolean isSnapshotCorrupted() { 192 return hfilesMissing.get() > 0 || logsMissing.get() > 0 || hfilesCorrupted.get() > 0; 193 } 194 195 /** Returns the number of available store files */ 196 public int getStoreFilesCount() { 197 return hfilesCount.get() + hfilesArchiveCount.get() + hfilesMobCount.get(); 198 } 199 200 /** Returns the number of available store files in the archive */ 201 public int getArchivedStoreFilesCount() { 202 return hfilesArchiveCount.get(); 203 } 204 205 /** Returns the number of available store files in the mob dir */ 206 public int getMobStoreFilesCount() { 207 return hfilesMobCount.get(); 208 } 209 210 /** Returns the number of available log files */ 211 public int getLogsCount() { 212 return logsCount.get(); 213 } 214 215 /** Returns the number of missing store files */ 216 public int getMissingStoreFilesCount() { 217 return hfilesMissing.get(); 218 } 219 220 /** Returns the number of corrupted store files */ 221 public int getCorruptedStoreFilesCount() { 222 return hfilesCorrupted.get(); 223 } 224 225 /** Returns the number of missing log files */ 226 public int getMissingLogsCount() { 227 return logsMissing.get(); 228 } 229 230 /** Returns the total size of the store files referenced by the snapshot */ 231 public long getStoreFilesSize() { 232 return hfilesSize.get() + hfilesArchiveSize.get() + hfilesMobSize.get(); 233 } 234 235 /** Returns the total size of the store files shared */ 236 public long getSharedStoreFilesSize() { 237 return hfilesSize.get(); 238 } 239 240 /** Returns the total size of the store files in the archive */ 241 public long getArchivedStoreFileSize() { 242 return hfilesArchiveSize.get(); 243 } 244 245 /** Returns the total size of the store files in the mob store */ 246 public long getMobStoreFilesSize() { 247 return hfilesMobSize.get(); 248 } 249 250 /** 251 * @return the total size of the store files in the archive which is not shared with other 252 * snapshots and tables This is only calculated when 253 * {@link #getSnapshotStats(Configuration, SnapshotProtos.SnapshotDescription, Map)} is 254 * called with a non-null Map 255 */ 256 public long getNonSharedArchivedStoreFilesSize() { 257 return nonSharedHfilesArchiveSize.get(); 258 } 259 260 /** Returns the percentage of the shared store files */ 261 public float getSharedStoreFilePercentage() { 262 return getStoreFilesSize() == 0 263 ? 0 264 : ((float) hfilesSize.get() / (getStoreFilesSize())) * 100; 265 } 266 267 /** Returns the percentage of the mob store files */ 268 public float getMobStoreFilePercentage() { 269 return getStoreFilesSize() == 0 270 ? 0 271 : ((float) hfilesMobSize.get() / (getStoreFilesSize())) * 100; 272 } 273 274 /** Returns the total log size */ 275 public long getLogsSize() { 276 return logSize.get(); 277 } 278 279 /** 280 * Check if for a give file in archive, if there are other snapshots/tables still reference it. 281 * @param filePath file path in archive 282 * @param snapshotFilesMap a map for store files in snapshots about how many snapshots refer to 283 * it. 284 * @return true or false 285 */ 286 private boolean isArchivedFileStillReferenced(final Path filePath, 287 final Map<Path, Integer> snapshotFilesMap) { 288 289 Integer c = snapshotFilesMap.get(filePath); 290 291 // Check if there are other snapshots or table from clone_snapshot() (via back-reference) 292 // still reference to it. 293 if ((c != null) && (c == 1)) { 294 Path parentDir = filePath.getParent(); 295 Path backRefDir = HFileLink.getBackReferencesDir(parentDir, filePath.getName()); 296 try { 297 if (CommonFSUtils.listStatus(fs, backRefDir) == null) { 298 return false; 299 } 300 } catch (IOException e) { 301 // For the purpose of this function, IOException is ignored and treated as 302 // the file is still being referenced. 303 } 304 } 305 return true; 306 } 307 308 /** 309 * Add the specified store file to the stats 310 * @param region region encoded Name 311 * @param family family name 312 * @param storeFile store file name 313 * @param filesMap store files map for all snapshots, it may be null 314 * @return the store file information 315 */ 316 FileInfo addStoreFile(final RegionInfo region, final String family, 317 final SnapshotRegionManifest.StoreFile storeFile, final Map<Path, Integer> filesMap) 318 throws IOException { 319 HFileLink link = 320 HFileLink.build(conf, snapshotTable, region.getEncodedName(), family, storeFile.getName()); 321 boolean isCorrupted = false; 322 boolean inArchive = false; 323 long size = -1; 324 try { 325 if (fs.exists(link.getArchivePath())) { 326 inArchive = true; 327 size = fs.getFileStatus(link.getArchivePath()).getLen(); 328 hfilesArchiveSize.addAndGet(size); 329 hfilesArchiveCount.incrementAndGet(); 330 331 // If store file is not shared with other snapshots and tables, 332 // increase nonSharedHfilesArchiveSize 333 if ( 334 (filesMap != null) && !isArchivedFileStillReferenced(link.getArchivePath(), filesMap) 335 ) { 336 nonSharedHfilesArchiveSize.addAndGet(size); 337 } 338 } else if (fs.exists(link.getMobPath())) { 339 inArchive = true; 340 size = fs.getFileStatus(link.getMobPath()).getLen(); 341 hfilesMobSize.addAndGet(size); 342 hfilesMobCount.incrementAndGet(); 343 } else { 344 size = link.getFileStatus(fs).getLen(); 345 hfilesSize.addAndGet(size); 346 hfilesCount.incrementAndGet(); 347 } 348 isCorrupted = (storeFile.hasFileSize() && storeFile.getFileSize() != size); 349 if (isCorrupted) hfilesCorrupted.incrementAndGet(); 350 } catch (FileNotFoundException e) { 351 hfilesMissing.incrementAndGet(); 352 } 353 return new FileInfo(inArchive, size, isCorrupted); 354 } 355 356 /** 357 * Add the specified log file to the stats 358 * @param server server name 359 * @param logfile log file name 360 * @return the log information 361 */ 362 FileInfo addLogFile(final String server, final String logfile) throws IOException { 363 WALLink logLink = new WALLink(conf, server, logfile); 364 long size = -1; 365 try { 366 size = logLink.getFileStatus(fs).getLen(); 367 logSize.addAndGet(size); 368 logsCount.incrementAndGet(); 369 } catch (FileNotFoundException e) { 370 logsMissing.incrementAndGet(); 371 } 372 return new FileInfo(false, size, false); 373 } 374 } 375 376 private FileSystem fs; 377 private Path rootDir; 378 379 private SnapshotManifest snapshotManifest; 380 381 private boolean listSnapshots = false; 382 private String snapshotName; 383 private Path remoteDir; 384 private boolean showSchema = false; 385 private boolean showFiles = false; 386 private boolean showStats = false; 387 private boolean printSizeInBytes = false; 388 389 @Override 390 public int doWork() throws IOException, InterruptedException { 391 if (remoteDir != null) { 392 URI defaultFs = remoteDir.getFileSystem(conf).getUri(); 393 CommonFSUtils.setFsDefault(conf, new Path(defaultFs)); 394 CommonFSUtils.setRootDir(conf, remoteDir); 395 } 396 397 // List Available Snapshots 398 if (listSnapshots) { 399 SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); 400 List<SnapshotDescription> snapshotDescriptions = getSnapshotList(conf); 401 System.out.printf("%-20s | %-20s | %-20s | %-20s | %s%n", "SNAPSHOT", "CREATION TIME", 402 "TTL(Sec)", "EXPIRED", "TABLE NAME"); 403 long currentTime = System.currentTimeMillis(); 404 for (SnapshotDescription desc : snapshotDescriptions) { 405 String ttlInfo = 406 desc.getTtl() == DEFAULT_SNAPSHOT_TTL ? TTL_FOREVER : String.valueOf(desc.getTtl()); 407 String expired = SnapshotDescriptionUtils.isExpiredSnapshot(desc.getTtl(), 408 desc.getCreationTime(), currentTime) ? EXPIRED_LABEL : NOT_EXPIRED_LABEL; 409 System.out.printf("%-20s | %20s | %20s | %20s | %s%n", desc.getName(), 410 df.format(new Date(desc.getCreationTime())), ttlInfo, expired, 411 desc.getTableNameAsString()); 412 } 413 return 0; 414 } 415 416 rootDir = CommonFSUtils.getRootDir(conf); 417 fs = FileSystem.get(rootDir.toUri(), conf); 418 LOG.debug("fs=" + fs.getUri().toString() + " root=" + rootDir); 419 420 // Load snapshot information 421 if (!loadSnapshotInfo(snapshotName)) { 422 System.err.println("Snapshot '" + snapshotName + "' not found!"); 423 return 1; 424 } 425 426 printInfo(); 427 if (showSchema) { 428 printSchema(); 429 } 430 printFiles(showFiles, showStats); 431 432 return 0; 433 } 434 435 /** 436 * Load snapshot info and table descriptor for the specified snapshot 437 * @param snapshotName name of the snapshot to load 438 * @return false if snapshot is not found 439 */ 440 private boolean loadSnapshotInfo(final String snapshotName) throws IOException { 441 Path snapshotDir = SnapshotDescriptionUtils.getCompletedSnapshotDir(snapshotName, rootDir); 442 if (!fs.exists(snapshotDir)) { 443 LOG.warn("Snapshot '" + snapshotName + "' not found in: " + snapshotDir); 444 return false; 445 } 446 447 SnapshotProtos.SnapshotDescription snapshotDesc = 448 SnapshotDescriptionUtils.readSnapshotInfo(fs, snapshotDir); 449 snapshotManifest = SnapshotManifest.open(getConf(), fs, snapshotDir, snapshotDesc); 450 return true; 451 } 452 453 /** 454 * Dump the {@link SnapshotDescription} 455 */ 456 private void printInfo() { 457 SnapshotProtos.SnapshotDescription snapshotDesc = snapshotManifest.getSnapshotDescription(); 458 SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); 459 String ttlInfo = snapshotDesc.getTtl() == DEFAULT_SNAPSHOT_TTL 460 ? TTL_FOREVER 461 : String.valueOf(snapshotDesc.getTtl()); 462 String expired = SnapshotDescriptionUtils.isExpiredSnapshot(snapshotDesc.getTtl(), 463 snapshotDesc.getCreationTime(), System.currentTimeMillis()) 464 ? EXPIRED_LABEL 465 : NOT_EXPIRED_LABEL; 466 System.out.println("Snapshot Info"); 467 System.out.println("-----------------------------------------"); 468 System.out.println(" Name: " + snapshotDesc.getName()); 469 System.out.println(" Type: " + snapshotDesc.getType()); 470 System.out.println(" Table: " + snapshotDesc.getTable()); 471 System.out.println(" Format: " + snapshotDesc.getVersion()); 472 System.out.println(" Created: " + df.format(new Date(snapshotDesc.getCreationTime()))); 473 System.out.println("Ttl(Sec): " + ttlInfo); 474 System.out.println(" Expired: " + expired); 475 System.out.println(" Owner: " + snapshotDesc.getOwner()); 476 System.out.println(); 477 } 478 479 /** 480 * Dump the {@link org.apache.hadoop.hbase.client.TableDescriptor} 481 */ 482 private void printSchema() { 483 System.out.println("Table Descriptor"); 484 System.out.println("----------------------------------------"); 485 System.out.println(snapshotManifest.getTableDescriptor().toString()); 486 System.out.println(); 487 } 488 489 /** 490 * Collect the hfiles and logs statistics of the snapshot and dump the file list if requested and 491 * the collected information. 492 */ 493 private void printFiles(final boolean showFiles, final boolean showStats) throws IOException { 494 if (showFiles) { 495 System.out.println("Snapshot Files"); 496 System.out.println("----------------------------------------"); 497 } 498 499 // Collect information about hfiles and logs in the snapshot 500 final SnapshotProtos.SnapshotDescription snapshotDesc = 501 snapshotManifest.getSnapshotDescription(); 502 final String table = snapshotDesc.getTable(); 503 final SnapshotDescription desc = ProtobufUtil.createSnapshotDesc(snapshotDesc); 504 final SnapshotStats stats = new SnapshotStats(this.getConf(), this.fs, desc); 505 SnapshotReferenceUtil.concurrentVisitReferencedFiles(getConf(), fs, snapshotManifest, 506 "SnapshotInfo", new SnapshotReferenceUtil.SnapshotVisitor() { 507 @Override 508 public void storeFile(final RegionInfo regionInfo, final String family, 509 final SnapshotRegionManifest.StoreFile storeFile) throws IOException { 510 if (storeFile.hasReference()) return; 511 512 SnapshotStats.FileInfo info = stats.addStoreFile(regionInfo, family, storeFile, null); 513 if (showFiles) { 514 String state = info.getStateToString(); 515 System.out.printf("%8s %s/%s/%s/%s %s%n", 516 (info.isMissing() ? "-" : fileSizeToString(info.getSize())), table, 517 regionInfo.getEncodedName(), family, storeFile.getName(), 518 state == null ? "" : "(" + state + ")"); 519 } 520 } 521 }); 522 523 // Dump the stats 524 System.out.println(); 525 if (stats.isSnapshotCorrupted()) { 526 System.out.println("**************************************************************"); 527 System.out.printf("BAD SNAPSHOT: %d hfile(s) and %d log(s) missing.%n", 528 stats.getMissingStoreFilesCount(), stats.getMissingLogsCount()); 529 System.out.printf(" %d hfile(s) corrupted.%n", 530 stats.getCorruptedStoreFilesCount()); 531 System.out.println("**************************************************************"); 532 } 533 534 if (showStats) { 535 System.out.printf( 536 "%d HFiles (%d in archive, %d in mob storage), total size %s " 537 + "(%.2f%% %s shared with the source table, %.2f%% %s in mob dir)%n", 538 stats.getStoreFilesCount(), stats.getArchivedStoreFilesCount(), 539 stats.getMobStoreFilesCount(), fileSizeToString(stats.getStoreFilesSize()), 540 stats.getSharedStoreFilePercentage(), fileSizeToString(stats.getSharedStoreFilesSize()), 541 stats.getMobStoreFilePercentage(), fileSizeToString(stats.getMobStoreFilesSize())); 542 System.out.printf("%d Logs, total size %s%n", stats.getLogsCount(), 543 fileSizeToString(stats.getLogsSize())); 544 System.out.println(); 545 } 546 } 547 548 private String fileSizeToString(long size) { 549 return printSizeInBytes ? Long.toString(size) : Strings.humanReadableInt(size); 550 } 551 552 @Override 553 protected void addOptions() { 554 addOption(Options.SNAPSHOT); 555 addOption(Options.REMOTE_DIR); 556 addOption(Options.LIST_SNAPSHOTS); 557 addOption(Options.FILES); 558 addOption(Options.STATS); 559 addOption(Options.SCHEMA); 560 addOption(Options.SIZE_IN_BYTES); 561 } 562 563 @Override 564 protected CommandLineParser newParser() { 565 // Commons-CLI lacks the capability to handle combinations of options, so we do it ourselves 566 // Validate in parse() to get helpful error messages instead of exploding in processOptions() 567 return new DefaultParser() { 568 @Override 569 public CommandLine parse(org.apache.hbase.thirdparty.org.apache.commons.cli.Options opts, 570 String[] args, Properties props, boolean stop) throws ParseException { 571 CommandLine cl = super.parse(opts, args, props, stop); 572 if (!cmd.hasOption(Options.LIST_SNAPSHOTS) && !cmd.hasOption(Options.SNAPSHOT)) { 573 throw new ParseException("Missing required snapshot option!"); 574 } 575 return cl; 576 } 577 }; 578 } 579 580 @Override 581 protected void processOptions(CommandLine cmd) { 582 snapshotName = cmd.getOptionValue(Options.SNAPSHOT.getLongOpt()); 583 showFiles = cmd.hasOption(Options.FILES.getLongOpt()); 584 showStats = 585 cmd.hasOption(Options.FILES.getLongOpt()) || cmd.hasOption(Options.STATS.getLongOpt()); 586 showSchema = cmd.hasOption(Options.SCHEMA.getLongOpt()); 587 listSnapshots = cmd.hasOption(Options.LIST_SNAPSHOTS.getLongOpt()); 588 printSizeInBytes = cmd.hasOption(Options.SIZE_IN_BYTES.getLongOpt()); 589 if (cmd.hasOption(Options.REMOTE_DIR.getLongOpt())) { 590 remoteDir = new Path(cmd.getOptionValue(Options.REMOTE_DIR.getLongOpt())); 591 } 592 } 593 594 @Override 595 protected void printUsage() { 596 printUsage("hbase snapshot info [options]", "Options:", ""); 597 System.err.println("Examples:"); 598 System.err.println(" hbase snapshot info --snapshot MySnapshot --files"); 599 } 600 601 /** 602 * Returns the snapshot stats 603 * @param conf the {@link Configuration} to use 604 * @param snapshot {@link SnapshotDescription} to get stats from 605 * @return the snapshot stats 606 */ 607 public static SnapshotStats getSnapshotStats(final Configuration conf, 608 final SnapshotDescription snapshot) throws IOException { 609 SnapshotProtos.SnapshotDescription snapshotDesc = 610 ProtobufUtil.createHBaseProtosSnapshotDesc(snapshot); 611 return getSnapshotStats(conf, snapshotDesc, null); 612 } 613 614 /** 615 * Returns the snapshot stats 616 * @param conf the {@link Configuration} to use 617 * @param snapshotDesc HBaseProtos.SnapshotDescription to get stats from 618 * @param filesMap {@link Map} store files map for all snapshots, it may be null 619 * @return the snapshot stats 620 */ 621 public static SnapshotStats getSnapshotStats(final Configuration conf, 622 final SnapshotProtos.SnapshotDescription snapshotDesc, final Map<Path, Integer> filesMap) 623 throws IOException { 624 Path rootDir = CommonFSUtils.getRootDir(conf); 625 FileSystem fs = FileSystem.get(rootDir.toUri(), conf); 626 Path snapshotDir = SnapshotDescriptionUtils.getCompletedSnapshotDir(snapshotDesc, rootDir); 627 SnapshotManifest manifest = SnapshotManifest.open(conf, fs, snapshotDir, snapshotDesc); 628 final SnapshotStats stats = new SnapshotStats(conf, fs, snapshotDesc); 629 SnapshotReferenceUtil.concurrentVisitReferencedFiles(conf, fs, manifest, 630 "SnapshotsStatsAggregation", new SnapshotReferenceUtil.SnapshotVisitor() { 631 @Override 632 public void storeFile(final RegionInfo regionInfo, final String family, 633 final SnapshotRegionManifest.StoreFile storeFile) throws IOException { 634 if (!storeFile.hasReference()) { 635 stats.addStoreFile(regionInfo, family, storeFile, filesMap); 636 } 637 } 638 }); 639 return stats; 640 } 641 642 /** 643 * Returns the list of available snapshots in the specified location 644 * @param conf the {@link Configuration} to use 645 * @return the list of snapshots 646 */ 647 public static List<SnapshotDescription> getSnapshotList(final Configuration conf) 648 throws IOException { 649 Path rootDir = CommonFSUtils.getRootDir(conf); 650 FileSystem fs = FileSystem.get(rootDir.toUri(), conf); 651 Path snapshotDir = SnapshotDescriptionUtils.getSnapshotsDir(rootDir); 652 FileStatus[] snapshots = CommonFSUtils.listStatus(fs, snapshotDir, 653 new SnapshotDescriptionUtils.CompletedSnaphotDirectoriesFilter(fs)); 654 if (snapshots == null) { 655 return Collections.emptyList(); 656 } 657 List<SnapshotDescription> snapshotLists = new ArrayList<>(snapshots.length); 658 for (FileStatus snapshotDirStat : snapshots) { 659 SnapshotProtos.SnapshotDescription snapshotDesc = 660 SnapshotDescriptionUtils.readSnapshotInfo(fs, snapshotDirStat.getPath()); 661 snapshotLists.add(ProtobufUtil.createSnapshotDesc(snapshotDesc)); 662 } 663 return snapshotLists; 664 } 665 666 /** 667 * Gets the store files map for snapshot 668 * @param conf the {@link Configuration} to use 669 * @param snapshot {@link SnapshotDescription} to get stats from 670 * @param exec the {@link ExecutorService} to use 671 * @param filesMap {@link Map} the map to put the mapping entries 672 * @param uniqueHFilesArchiveSize {@link AtomicLong} the accumulated store file size in archive 673 * @param uniqueHFilesSize {@link AtomicLong} the accumulated store file size shared 674 * @param uniqueHFilesMobSize {@link AtomicLong} the accumulated mob store file size shared 675 */ 676 private static void getSnapshotFilesMap(final Configuration conf, 677 final SnapshotDescription snapshot, final ExecutorService exec, 678 final ConcurrentHashMap<Path, Integer> filesMap, final AtomicLong uniqueHFilesArchiveSize, 679 final AtomicLong uniqueHFilesSize, final AtomicLong uniqueHFilesMobSize) throws IOException { 680 SnapshotProtos.SnapshotDescription snapshotDesc = 681 ProtobufUtil.createHBaseProtosSnapshotDesc(snapshot); 682 Path rootDir = CommonFSUtils.getRootDir(conf); 683 final FileSystem fs = FileSystem.get(rootDir.toUri(), conf); 684 685 Path snapshotDir = SnapshotDescriptionUtils.getCompletedSnapshotDir(snapshotDesc, rootDir); 686 SnapshotManifest manifest = SnapshotManifest.open(conf, fs, snapshotDir, snapshotDesc); 687 SnapshotReferenceUtil.concurrentVisitReferencedFiles(conf, fs, manifest, exec, 688 new SnapshotReferenceUtil.SnapshotVisitor() { 689 @Override 690 public void storeFile(final RegionInfo regionInfo, final String family, 691 final SnapshotRegionManifest.StoreFile storeFile) throws IOException { 692 if (!storeFile.hasReference()) { 693 HFileLink link = HFileLink.build(conf, snapshot.getTableName(), 694 regionInfo.getEncodedName(), family, storeFile.getName()); 695 long size; 696 Integer count; 697 Path p; 698 AtomicLong al; 699 int c = 0; 700 701 if (fs.exists(link.getArchivePath())) { 702 p = link.getArchivePath(); 703 al = uniqueHFilesArchiveSize; 704 size = fs.getFileStatus(p).getLen(); 705 } else if (fs.exists(link.getMobPath())) { 706 p = link.getMobPath(); 707 al = uniqueHFilesMobSize; 708 size = fs.getFileStatus(p).getLen(); 709 } else { 710 p = link.getOriginPath(); 711 al = uniqueHFilesSize; 712 size = link.getFileStatus(fs).getLen(); 713 } 714 715 // If it has been counted, do not double count 716 count = filesMap.get(p); 717 if (count != null) { 718 c = count.intValue(); 719 } else { 720 al.addAndGet(size); 721 } 722 723 filesMap.put(p, ++c); 724 } 725 } 726 }); 727 } 728 729 /** 730 * Returns the map of store files based on path for all snapshots 731 * @param conf the {@link Configuration} to use 732 * @param uniqueHFilesArchiveSize pass out the size for store files in archive 733 * @param uniqueHFilesSize pass out the size for store files shared 734 * @param uniqueHFilesMobSize pass out the size for mob store files shared 735 * @return the map of store files 736 */ 737 public static Map<Path, Integer> getSnapshotsFilesMap(final Configuration conf, 738 AtomicLong uniqueHFilesArchiveSize, AtomicLong uniqueHFilesSize, AtomicLong uniqueHFilesMobSize) 739 throws IOException { 740 List<SnapshotDescription> snapshotList = getSnapshotList(conf); 741 742 if (snapshotList.isEmpty()) { 743 return Collections.emptyMap(); 744 } 745 746 ConcurrentHashMap<Path, Integer> fileMap = new ConcurrentHashMap<>(); 747 748 ExecutorService exec = SnapshotManifest.createExecutor(conf, "SnapshotsFilesMapping"); 749 750 try { 751 for (final SnapshotDescription snapshot : snapshotList) { 752 getSnapshotFilesMap(conf, snapshot, exec, fileMap, uniqueHFilesArchiveSize, 753 uniqueHFilesSize, uniqueHFilesMobSize); 754 } 755 } finally { 756 exec.shutdown(); 757 } 758 759 return fileMap; 760 } 761 762 public static void main(String[] args) { 763 new SnapshotInfo().doStaticMain(args); 764 } 765}