| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.hadoop.hbase.snapshot; |
| |
| import java.io.FileNotFoundException; |
| import java.io.IOException; |
| import java.net.URI; |
| import java.text.SimpleDateFormat; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.Date; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Properties; |
| import java.util.concurrent.ConcurrentHashMap; |
| import java.util.concurrent.ExecutorService; |
| import java.util.concurrent.atomic.AtomicInteger; |
| import java.util.concurrent.atomic.AtomicLong; |
| import org.apache.hadoop.conf.Configuration; |
| import org.apache.hadoop.fs.FileStatus; |
| import org.apache.hadoop.fs.FileSystem; |
| import org.apache.hadoop.fs.Path; |
| import org.apache.hadoop.hbase.TableName; |
| import org.apache.hadoop.hbase.client.RegionInfo; |
| import org.apache.hadoop.hbase.client.SnapshotDescription; |
| import org.apache.hadoop.hbase.io.HFileLink; |
| import org.apache.hadoop.hbase.io.WALLink; |
| import org.apache.hadoop.hbase.util.AbstractHBaseTool; |
| import org.apache.hadoop.hbase.util.CommonFSUtils; |
| import org.apache.hadoop.util.StringUtils; |
| import org.apache.yetus.audience.InterfaceAudience; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| import org.apache.hbase.thirdparty.org.apache.commons.cli.CommandLine; |
| import org.apache.hbase.thirdparty.org.apache.commons.cli.CommandLineParser; |
| import org.apache.hbase.thirdparty.org.apache.commons.cli.DefaultParser; |
| import org.apache.hbase.thirdparty.org.apache.commons.cli.Option; |
| import org.apache.hbase.thirdparty.org.apache.commons.cli.Options; |
| import org.apache.hbase.thirdparty.org.apache.commons.cli.ParseException; |
| |
| import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil; |
| import org.apache.hadoop.hbase.shaded.protobuf.generated.SnapshotProtos; |
| import org.apache.hadoop.hbase.shaded.protobuf.generated.SnapshotProtos.SnapshotRegionManifest; |
| |
| /** |
| * Tool for dumping snapshot information. |
| * <ol> |
| * <li>Table Descriptor |
| * <li>Snapshot creation time, type, format version, ... |
| * <li>List of hfiles and wals |
| * <li>Stats about hfiles and logs sizes, percentage of shared with the source table, ... |
| * </ol> |
| */ |
| @InterfaceAudience.Public |
| public final class SnapshotInfo extends AbstractHBaseTool { |
| private static final Logger LOG = LoggerFactory.getLogger(SnapshotInfo.class); |
| |
| static final class Options { |
| static final Option SNAPSHOT = |
| new Option(null, "snapshot", true, "The name of the snapshot to be detailed."); |
| static final Option REMOTE_DIR = |
| new Option(null, "remote-dir", true, "A custom root directory where snapshots are stored. " |
| + "Use it together with the --snapshot option."); |
| static final Option LIST_SNAPSHOTS = |
| new Option(null, "list-snapshots", false, "List all the available snapshots and exit."); |
| static final Option FILES = |
| new Option(null, "files", false, "The list of files retained by the specified snapshot. " |
| + "Use it together with the --snapshot option."); |
| static final Option STATS = |
| new Option(null, "stats", false, "Additional information about the specified snapshot. " |
| + "Use it together with the --snapshot option."); |
| static final Option SCHEMA = new Option(null, "schema", false, |
| "Show the descriptor of the table for the specified snapshot. " |
| + "Use it together with the --snapshot option."); |
| static final Option SIZE_IN_BYTES = |
| new Option(null, "size-in-bytes", false, "Print the size of the files in bytes. " |
| + "Use it together with the --snapshot and --files options."); |
| } |
| |
| /** |
| * Statistics about the snapshot |
| * <ol> |
| * <li>How many store files and logs are in the archive |
| * <li>How many store files and logs are shared with the table |
| * <li>Total store files and logs size and shared amount |
| * </ol> |
| */ |
| public static class SnapshotStats { |
| /** Information about the file referenced by the snapshot */ |
| static class FileInfo { |
| private final boolean corrupted; |
| private final boolean inArchive; |
| private final long size; |
| |
| FileInfo(final boolean inArchive, final long size, final boolean corrupted) { |
| this.corrupted = corrupted; |
| this.inArchive = inArchive; |
| this.size = size; |
| } |
| |
| /** Returns true if the file is in the archive */ |
| public boolean inArchive() { |
| return this.inArchive; |
| } |
| |
| /** Returns true if the file is corrupted */ |
| public boolean isCorrupted() { |
| return this.corrupted; |
| } |
| |
| /** Returns true if the file is missing */ |
| public boolean isMissing() { |
| return this.size < 0; |
| } |
| |
| /** Returns the file size */ |
| public long getSize() { |
| return this.size; |
| } |
| |
| String getStateToString() { |
| if (isCorrupted()) return "CORRUPTED"; |
| if (isMissing()) return "NOT FOUND"; |
| if (inArchive()) return "archive"; |
| return null; |
| } |
| } |
| |
| private AtomicInteger hfilesArchiveCount = new AtomicInteger(); |
| private AtomicInteger hfilesCorrupted = new AtomicInteger(); |
| private AtomicInteger hfilesMissing = new AtomicInteger(); |
| private AtomicInteger hfilesCount = new AtomicInteger(); |
| private AtomicInteger hfilesMobCount = new AtomicInteger(); |
| private AtomicInteger logsMissing = new AtomicInteger(); |
| private AtomicInteger logsCount = new AtomicInteger(); |
| private AtomicLong hfilesArchiveSize = new AtomicLong(); |
| private AtomicLong hfilesSize = new AtomicLong(); |
| private AtomicLong hfilesMobSize = new AtomicLong(); |
| private AtomicLong nonSharedHfilesArchiveSize = new AtomicLong(); |
| private AtomicLong logSize = new AtomicLong(); |
| |
| private final SnapshotProtos.SnapshotDescription snapshot; |
| private final TableName snapshotTable; |
| private final Configuration conf; |
| private final FileSystem fs; |
| |
| SnapshotStats(final Configuration conf, final FileSystem fs, |
| final SnapshotDescription snapshot) { |
| this.snapshot = ProtobufUtil.createHBaseProtosSnapshotDesc(snapshot); |
| this.snapshotTable = snapshot.getTableName(); |
| this.conf = conf; |
| this.fs = fs; |
| } |
| |
| SnapshotStats(final Configuration conf, final FileSystem fs, |
| final SnapshotProtos.SnapshotDescription snapshot) { |
| this.snapshot = snapshot; |
| this.snapshotTable = TableName.valueOf(snapshot.getTable()); |
| this.conf = conf; |
| this.fs = fs; |
| } |
| |
| /** Returns the snapshot descriptor */ |
| public SnapshotDescription getSnapshotDescription() { |
| return ProtobufUtil.createSnapshotDesc(this.snapshot); |
| } |
| |
| /** Returns true if the snapshot is corrupted */ |
| public boolean isSnapshotCorrupted() { |
| return hfilesMissing.get() > 0 || logsMissing.get() > 0 || hfilesCorrupted.get() > 0; |
| } |
| |
| /** Returns the number of available store files */ |
| public int getStoreFilesCount() { |
| return hfilesCount.get() + hfilesArchiveCount.get() + hfilesMobCount.get(); |
| } |
| |
| /** Returns the number of available store files in the archive */ |
| public int getArchivedStoreFilesCount() { |
| return hfilesArchiveCount.get(); |
| } |
| |
| /** Returns the number of available store files in the mob dir */ |
| public int getMobStoreFilesCount() { |
| return hfilesMobCount.get(); |
| } |
| |
| /** Returns the number of available log files */ |
| public int getLogsCount() { |
| return logsCount.get(); |
| } |
| |
| /** Returns the number of missing store files */ |
| public int getMissingStoreFilesCount() { |
| return hfilesMissing.get(); |
| } |
| |
| /** Returns the number of corrupted store files */ |
| public int getCorruptedStoreFilesCount() { |
| return hfilesCorrupted.get(); |
| } |
| |
| /** Returns the number of missing log files */ |
| public int getMissingLogsCount() { |
| return logsMissing.get(); |
| } |
| |
| /** Returns the total size of the store files referenced by the snapshot */ |
| public long getStoreFilesSize() { |
| return hfilesSize.get() + hfilesArchiveSize.get() + hfilesMobSize.get(); |
| } |
| |
| /** Returns the total size of the store files shared */ |
| public long getSharedStoreFilesSize() { |
| return hfilesSize.get(); |
| } |
| |
| /** Returns the total size of the store files in the archive */ |
| public long getArchivedStoreFileSize() { |
| return hfilesArchiveSize.get(); |
| } |
| |
| /** Returns the total size of the store files in the mob store */ |
| public long getMobStoreFilesSize() { |
| return hfilesMobSize.get(); |
| } |
| |
| /** |
| * @return the total size of the store files in the archive which is not shared with other |
| * snapshots and tables This is only calculated when |
| * {@link #getSnapshotStats(Configuration, SnapshotProtos.SnapshotDescription, Map)} is |
| * called with a non-null Map |
| */ |
| public long getNonSharedArchivedStoreFilesSize() { |
| return nonSharedHfilesArchiveSize.get(); |
| } |
| |
| /** Returns the percentage of the shared store files */ |
| public float getSharedStoreFilePercentage() { |
| return ((float) hfilesSize.get() / (getStoreFilesSize())) * 100; |
| } |
| |
| /** Returns the percentage of the mob store files */ |
| public float getMobStoreFilePercentage() { |
| return ((float) hfilesMobSize.get() / (getStoreFilesSize())) * 100; |
| } |
| |
| /** Returns the total log size */ |
| public long getLogsSize() { |
| return logSize.get(); |
| } |
| |
| /** |
| * Check if for a give file in archive, if there are other snapshots/tables still reference it. |
| * @param filePath file path in archive |
| * @param snapshotFilesMap a map for store files in snapshots about how many snapshots refer to |
| * it. |
| * @return true or false |
| */ |
| private boolean isArchivedFileStillReferenced(final Path filePath, |
| final Map<Path, Integer> snapshotFilesMap) { |
| |
| Integer c = snapshotFilesMap.get(filePath); |
| |
| // Check if there are other snapshots or table from clone_snapshot() (via back-reference) |
| // still reference to it. |
| if ((c != null) && (c == 1)) { |
| Path parentDir = filePath.getParent(); |
| Path backRefDir = HFileLink.getBackReferencesDir(parentDir, filePath.getName()); |
| try { |
| if (CommonFSUtils.listStatus(fs, backRefDir) == null) { |
| return false; |
| } |
| } catch (IOException e) { |
| // For the purpose of this function, IOException is ignored and treated as |
| // the file is still being referenced. |
| } |
| } |
| return true; |
| } |
| |
| /** |
| * Add the specified store file to the stats |
| * @param region region encoded Name |
| * @param family family name |
| * @param storeFile store file name |
| * @param filesMap store files map for all snapshots, it may be null |
| * @return the store file information |
| */ |
| FileInfo addStoreFile(final RegionInfo region, final String family, |
| final SnapshotRegionManifest.StoreFile storeFile, final Map<Path, Integer> filesMap) |
| throws IOException { |
| HFileLink link = |
| HFileLink.build(conf, snapshotTable, region.getEncodedName(), family, storeFile.getName()); |
| boolean isCorrupted = false; |
| boolean inArchive = false; |
| long size = -1; |
| try { |
| if (fs.exists(link.getArchivePath())) { |
| inArchive = true; |
| size = fs.getFileStatus(link.getArchivePath()).getLen(); |
| hfilesArchiveSize.addAndGet(size); |
| hfilesArchiveCount.incrementAndGet(); |
| |
| // If store file is not shared with other snapshots and tables, |
| // increase nonSharedHfilesArchiveSize |
| if ( |
| (filesMap != null) && !isArchivedFileStillReferenced(link.getArchivePath(), filesMap) |
| ) { |
| nonSharedHfilesArchiveSize.addAndGet(size); |
| } |
| } else if (fs.exists(link.getMobPath())) { |
| inArchive = true; |
| size = fs.getFileStatus(link.getMobPath()).getLen(); |
| hfilesMobSize.addAndGet(size); |
| hfilesMobCount.incrementAndGet(); |
| } else { |
| size = link.getFileStatus(fs).getLen(); |
| hfilesSize.addAndGet(size); |
| hfilesCount.incrementAndGet(); |
| } |
| isCorrupted = (storeFile.hasFileSize() && storeFile.getFileSize() != size); |
| if (isCorrupted) hfilesCorrupted.incrementAndGet(); |
| } catch (FileNotFoundException e) { |
| hfilesMissing.incrementAndGet(); |
| } |
| return new FileInfo(inArchive, size, isCorrupted); |
| } |
| |
| /** |
| * Add the specified log file to the stats |
| * @param server server name |
| * @param logfile log file name |
| * @return the log information |
| */ |
| FileInfo addLogFile(final String server, final String logfile) throws IOException { |
| WALLink logLink = new WALLink(conf, server, logfile); |
| long size = -1; |
| try { |
| size = logLink.getFileStatus(fs).getLen(); |
| logSize.addAndGet(size); |
| logsCount.incrementAndGet(); |
| } catch (FileNotFoundException e) { |
| logsMissing.incrementAndGet(); |
| } |
| return new FileInfo(false, size, false); |
| } |
| } |
| |
| private FileSystem fs; |
| private Path rootDir; |
| |
| private SnapshotManifest snapshotManifest; |
| |
| private boolean listSnapshots = false; |
| private String snapshotName; |
| private Path remoteDir; |
| private boolean showSchema = false; |
| private boolean showFiles = false; |
| private boolean showStats = false; |
| private boolean printSizeInBytes = false; |
| |
| @Override |
| public int doWork() throws IOException, InterruptedException { |
| if (remoteDir != null) { |
| URI defaultFs = remoteDir.getFileSystem(conf).getUri(); |
| CommonFSUtils.setFsDefault(conf, new Path(defaultFs)); |
| CommonFSUtils.setRootDir(conf, remoteDir); |
| } |
| |
| // List Available Snapshots |
| if (listSnapshots) { |
| SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); |
| System.out.printf("%-20s | %-20s | %-20s | %s%n", "SNAPSHOT", "CREATION TIME", "TTL IN SEC", |
| "TABLE NAME"); |
| for (SnapshotDescription desc : getSnapshotList(conf)) { |
| System.out.printf("%-20s | %20s | %20s | %s%n", desc.getName(), |
| df.format(new Date(desc.getCreationTime())), desc.getTtl(), desc.getTableNameAsString()); |
| } |
| return 0; |
| } |
| |
| rootDir = CommonFSUtils.getRootDir(conf); |
| fs = FileSystem.get(rootDir.toUri(), conf); |
| LOG.debug("fs=" + fs.getUri().toString() + " root=" + rootDir); |
| |
| // Load snapshot information |
| if (!loadSnapshotInfo(snapshotName)) { |
| System.err.println("Snapshot '" + snapshotName + "' not found!"); |
| return 1; |
| } |
| |
| printInfo(); |
| if (showSchema) { |
| printSchema(); |
| } |
| printFiles(showFiles, showStats); |
| |
| return 0; |
| } |
| |
| /** |
| * Load snapshot info and table descriptor for the specified snapshot |
| * @param snapshotName name of the snapshot to load |
| * @return false if snapshot is not found |
| */ |
| private boolean loadSnapshotInfo(final String snapshotName) throws IOException { |
| Path snapshotDir = SnapshotDescriptionUtils.getCompletedSnapshotDir(snapshotName, rootDir); |
| if (!fs.exists(snapshotDir)) { |
| LOG.warn("Snapshot '" + snapshotName + "' not found in: " + snapshotDir); |
| return false; |
| } |
| |
| SnapshotProtos.SnapshotDescription snapshotDesc = |
| SnapshotDescriptionUtils.readSnapshotInfo(fs, snapshotDir); |
| snapshotManifest = SnapshotManifest.open(getConf(), fs, snapshotDir, snapshotDesc); |
| return true; |
| } |
| |
| /** |
| * Dump the {@link SnapshotDescription} |
| */ |
| private void printInfo() { |
| SnapshotProtos.SnapshotDescription snapshotDesc = snapshotManifest.getSnapshotDescription(); |
| SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); |
| System.out.println("Snapshot Info"); |
| System.out.println("----------------------------------------"); |
| System.out.println(" Name: " + snapshotDesc.getName()); |
| System.out.println(" Type: " + snapshotDesc.getType()); |
| System.out.println(" Table: " + snapshotDesc.getTable()); |
| System.out.println(" Format: " + snapshotDesc.getVersion()); |
| System.out.println("Created: " + df.format(new Date(snapshotDesc.getCreationTime()))); |
| System.out.println(" Ttl: " + snapshotDesc.getTtl()); |
| System.out.println(" Owner: " + snapshotDesc.getOwner()); |
| System.out.println(); |
| } |
| |
| /** |
| * Dump the {@link org.apache.hadoop.hbase.client.TableDescriptor} |
| */ |
| private void printSchema() { |
| System.out.println("Table Descriptor"); |
| System.out.println("----------------------------------------"); |
| System.out.println(snapshotManifest.getTableDescriptor().toString()); |
| System.out.println(); |
| } |
| |
| /** |
| * Collect the hfiles and logs statistics of the snapshot and dump the file list if requested and |
| * the collected information. |
| */ |
| private void printFiles(final boolean showFiles, final boolean showStats) throws IOException { |
| if (showFiles) { |
| System.out.println("Snapshot Files"); |
| System.out.println("----------------------------------------"); |
| } |
| |
| // Collect information about hfiles and logs in the snapshot |
| final SnapshotProtos.SnapshotDescription snapshotDesc = |
| snapshotManifest.getSnapshotDescription(); |
| final String table = snapshotDesc.getTable(); |
| final SnapshotDescription desc = ProtobufUtil.createSnapshotDesc(snapshotDesc); |
| final SnapshotStats stats = new SnapshotStats(this.getConf(), this.fs, desc); |
| SnapshotReferenceUtil.concurrentVisitReferencedFiles(getConf(), fs, snapshotManifest, |
| "SnapshotInfo", new SnapshotReferenceUtil.SnapshotVisitor() { |
| @Override |
| public void storeFile(final RegionInfo regionInfo, final String family, |
| final SnapshotRegionManifest.StoreFile storeFile) throws IOException { |
| if (storeFile.hasReference()) return; |
| |
| SnapshotStats.FileInfo info = stats.addStoreFile(regionInfo, family, storeFile, null); |
| if (showFiles) { |
| String state = info.getStateToString(); |
| System.out.printf("%8s %s/%s/%s/%s %s%n", |
| (info.isMissing() ? "-" : fileSizeToString(info.getSize())), table, |
| regionInfo.getEncodedName(), family, storeFile.getName(), |
| state == null ? "" : "(" + state + ")"); |
| } |
| } |
| }); |
| |
| // Dump the stats |
| System.out.println(); |
| if (stats.isSnapshotCorrupted()) { |
| System.out.println("**************************************************************"); |
| System.out.printf("BAD SNAPSHOT: %d hfile(s) and %d log(s) missing.%n", |
| stats.getMissingStoreFilesCount(), stats.getMissingLogsCount()); |
| System.out.printf(" %d hfile(s) corrupted.%n", |
| stats.getCorruptedStoreFilesCount()); |
| System.out.println("**************************************************************"); |
| } |
| |
| if (showStats) { |
| System.out.printf( |
| "%d HFiles (%d in archive, %d in mob storage), total size %s " |
| + "(%.2f%% %s shared with the source table, %.2f%% %s in mob dir)%n", |
| stats.getStoreFilesCount(), stats.getArchivedStoreFilesCount(), |
| stats.getMobStoreFilesCount(), fileSizeToString(stats.getStoreFilesSize()), |
| stats.getSharedStoreFilePercentage(), fileSizeToString(stats.getSharedStoreFilesSize()), |
| stats.getMobStoreFilePercentage(), fileSizeToString(stats.getMobStoreFilesSize())); |
| System.out.printf("%d Logs, total size %s%n", stats.getLogsCount(), |
| fileSizeToString(stats.getLogsSize())); |
| System.out.println(); |
| } |
| } |
| |
| private String fileSizeToString(long size) { |
| return printSizeInBytes ? Long.toString(size) : StringUtils.humanReadableInt(size); |
| } |
| |
| @Override |
| protected void addOptions() { |
| addOption(Options.SNAPSHOT); |
| addOption(Options.REMOTE_DIR); |
| addOption(Options.LIST_SNAPSHOTS); |
| addOption(Options.FILES); |
| addOption(Options.STATS); |
| addOption(Options.SCHEMA); |
| addOption(Options.SIZE_IN_BYTES); |
| } |
| |
| @Override |
| protected CommandLineParser newParser() { |
| // Commons-CLI lacks the capability to handle combinations of options, so we do it ourselves |
| // Validate in parse() to get helpful error messages instead of exploding in processOptions() |
| return new DefaultParser() { |
| @Override |
| public CommandLine parse(org.apache.hbase.thirdparty.org.apache.commons.cli.Options opts, |
| String[] args, Properties props, boolean stop) throws ParseException { |
| CommandLine cl = super.parse(opts, args, props, stop); |
| if (!cmd.hasOption(Options.LIST_SNAPSHOTS) && !cmd.hasOption(Options.SNAPSHOT)) { |
| throw new ParseException("Missing required snapshot option!"); |
| } |
| return cl; |
| } |
| }; |
| } |
| |
| @Override |
| protected void processOptions(CommandLine cmd) { |
| snapshotName = cmd.getOptionValue(Options.SNAPSHOT.getLongOpt()); |
| showFiles = cmd.hasOption(Options.FILES.getLongOpt()); |
| showStats = |
| cmd.hasOption(Options.FILES.getLongOpt()) || cmd.hasOption(Options.STATS.getLongOpt()); |
| showSchema = cmd.hasOption(Options.SCHEMA.getLongOpt()); |
| listSnapshots = cmd.hasOption(Options.LIST_SNAPSHOTS.getLongOpt()); |
| printSizeInBytes = cmd.hasOption(Options.SIZE_IN_BYTES.getLongOpt()); |
| if (cmd.hasOption(Options.REMOTE_DIR.getLongOpt())) { |
| remoteDir = new Path(cmd.getOptionValue(Options.REMOTE_DIR.getLongOpt())); |
| } |
| } |
| |
| @Override |
| protected void printUsage() { |
| printUsage("hbase snapshot info [options]", "Options:", ""); |
| System.err.println("Examples:"); |
| System.err.println(" hbase snapshot info --snapshot MySnapshot --files"); |
| } |
| |
| /** |
| * Returns the snapshot stats |
| * @param conf the {@link Configuration} to use |
| * @param snapshot {@link SnapshotDescription} to get stats from |
| * @return the snapshot stats |
| */ |
| public static SnapshotStats getSnapshotStats(final Configuration conf, |
| final SnapshotDescription snapshot) throws IOException { |
| SnapshotProtos.SnapshotDescription snapshotDesc = |
| ProtobufUtil.createHBaseProtosSnapshotDesc(snapshot); |
| return getSnapshotStats(conf, snapshotDesc, null); |
| } |
| |
| /** |
| * Returns the snapshot stats |
| * @param conf the {@link Configuration} to use |
| * @param snapshotDesc HBaseProtos.SnapshotDescription to get stats from |
| * @param filesMap {@link Map} store files map for all snapshots, it may be null |
| * @return the snapshot stats |
| */ |
| public static SnapshotStats getSnapshotStats(final Configuration conf, |
| final SnapshotProtos.SnapshotDescription snapshotDesc, final Map<Path, Integer> filesMap) |
| throws IOException { |
| Path rootDir = CommonFSUtils.getRootDir(conf); |
| FileSystem fs = FileSystem.get(rootDir.toUri(), conf); |
| Path snapshotDir = SnapshotDescriptionUtils.getCompletedSnapshotDir(snapshotDesc, rootDir); |
| SnapshotManifest manifest = SnapshotManifest.open(conf, fs, snapshotDir, snapshotDesc); |
| final SnapshotStats stats = new SnapshotStats(conf, fs, snapshotDesc); |
| SnapshotReferenceUtil.concurrentVisitReferencedFiles(conf, fs, manifest, |
| "SnapshotsStatsAggregation", new SnapshotReferenceUtil.SnapshotVisitor() { |
| @Override |
| public void storeFile(final RegionInfo regionInfo, final String family, |
| final SnapshotRegionManifest.StoreFile storeFile) throws IOException { |
| if (!storeFile.hasReference()) { |
| stats.addStoreFile(regionInfo, family, storeFile, filesMap); |
| } |
| } |
| }); |
| return stats; |
| } |
| |
| /** |
| * Returns the list of available snapshots in the specified location |
| * @param conf the {@link Configuration} to use |
| * @return the list of snapshots |
| */ |
| public static List<SnapshotDescription> getSnapshotList(final Configuration conf) |
| throws IOException { |
| Path rootDir = CommonFSUtils.getRootDir(conf); |
| FileSystem fs = FileSystem.get(rootDir.toUri(), conf); |
| Path snapshotDir = SnapshotDescriptionUtils.getSnapshotsDir(rootDir); |
| FileStatus[] snapshots = fs.listStatus(snapshotDir, |
| new SnapshotDescriptionUtils.CompletedSnaphotDirectoriesFilter(fs)); |
| List<SnapshotDescription> snapshotLists = new ArrayList<>(snapshots.length); |
| for (FileStatus snapshotDirStat : snapshots) { |
| SnapshotProtos.SnapshotDescription snapshotDesc = |
| SnapshotDescriptionUtils.readSnapshotInfo(fs, snapshotDirStat.getPath()); |
| snapshotLists.add(ProtobufUtil.createSnapshotDesc(snapshotDesc)); |
| } |
| return snapshotLists; |
| } |
| |
| /** |
| * Gets the store files map for snapshot |
| * @param conf the {@link Configuration} to use |
| * @param snapshot {@link SnapshotDescription} to get stats from |
| * @param exec the {@link ExecutorService} to use |
| * @param filesMap {@link Map} the map to put the mapping entries |
| * @param uniqueHFilesArchiveSize {@link AtomicLong} the accumulated store file size in archive |
| * @param uniqueHFilesSize {@link AtomicLong} the accumulated store file size shared |
| * @param uniqueHFilesMobSize {@link AtomicLong} the accumulated mob store file size shared |
| */ |
| private static void getSnapshotFilesMap(final Configuration conf, |
| final SnapshotDescription snapshot, final ExecutorService exec, |
| final ConcurrentHashMap<Path, Integer> filesMap, final AtomicLong uniqueHFilesArchiveSize, |
| final AtomicLong uniqueHFilesSize, final AtomicLong uniqueHFilesMobSize) throws IOException { |
| SnapshotProtos.SnapshotDescription snapshotDesc = |
| ProtobufUtil.createHBaseProtosSnapshotDesc(snapshot); |
| Path rootDir = CommonFSUtils.getRootDir(conf); |
| final FileSystem fs = FileSystem.get(rootDir.toUri(), conf); |
| |
| Path snapshotDir = SnapshotDescriptionUtils.getCompletedSnapshotDir(snapshotDesc, rootDir); |
| SnapshotManifest manifest = SnapshotManifest.open(conf, fs, snapshotDir, snapshotDesc); |
| SnapshotReferenceUtil.concurrentVisitReferencedFiles(conf, fs, manifest, exec, |
| new SnapshotReferenceUtil.SnapshotVisitor() { |
| @Override |
| public void storeFile(final RegionInfo regionInfo, final String family, |
| final SnapshotRegionManifest.StoreFile storeFile) throws IOException { |
| if (!storeFile.hasReference()) { |
| HFileLink link = HFileLink.build(conf, snapshot.getTableName(), |
| regionInfo.getEncodedName(), family, storeFile.getName()); |
| long size; |
| Integer count; |
| Path p; |
| AtomicLong al; |
| int c = 0; |
| |
| if (fs.exists(link.getArchivePath())) { |
| p = link.getArchivePath(); |
| al = uniqueHFilesArchiveSize; |
| size = fs.getFileStatus(p).getLen(); |
| } else if (fs.exists(link.getMobPath())) { |
| p = link.getMobPath(); |
| al = uniqueHFilesMobSize; |
| size = fs.getFileStatus(p).getLen(); |
| } else { |
| p = link.getOriginPath(); |
| al = uniqueHFilesSize; |
| size = link.getFileStatus(fs).getLen(); |
| } |
| |
| // If it has been counted, do not double count |
| count = filesMap.get(p); |
| if (count != null) { |
| c = count.intValue(); |
| } else { |
| al.addAndGet(size); |
| } |
| |
| filesMap.put(p, ++c); |
| } |
| } |
| }); |
| } |
| |
| /** |
| * Returns the map of store files based on path for all snapshots |
| * @param conf the {@link Configuration} to use |
| * @param uniqueHFilesArchiveSize pass out the size for store files in archive |
| * @param uniqueHFilesSize pass out the size for store files shared |
| * @param uniqueHFilesMobSize pass out the size for mob store files shared |
| * @return the map of store files |
| */ |
| public static Map<Path, Integer> getSnapshotsFilesMap(final Configuration conf, |
| AtomicLong uniqueHFilesArchiveSize, AtomicLong uniqueHFilesSize, AtomicLong uniqueHFilesMobSize) |
| throws IOException { |
| List<SnapshotDescription> snapshotList = getSnapshotList(conf); |
| |
| if (snapshotList.isEmpty()) { |
| return Collections.emptyMap(); |
| } |
| |
| ConcurrentHashMap<Path, Integer> fileMap = new ConcurrentHashMap<>(); |
| |
| ExecutorService exec = SnapshotManifest.createExecutor(conf, "SnapshotsFilesMapping"); |
| |
| try { |
| for (final SnapshotDescription snapshot : snapshotList) { |
| getSnapshotFilesMap(conf, snapshot, exec, fileMap, uniqueHFilesArchiveSize, |
| uniqueHFilesSize, uniqueHFilesMobSize); |
| } |
| } finally { |
| exec.shutdown(); |
| } |
| |
| return fileMap; |
| } |
| |
| public static void main(String[] args) { |
| new SnapshotInfo().doStaticMain(args); |
| } |
| } |