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