private void refreshCache() throws IOException {
    // just list the snapshot directory directly, do not check the modification time for the root
    // snapshot directory, as some file system implementations do not modify the parent directory's
    // modTime when there are new sub items, for example, S3.
    FileStatus[] snapshotDirs = CommonFSUtils.listStatus(fs, snapshotDir,
      p -> !p.getName().equals(SnapshotDescriptionUtils.SNAPSHOT_TMP_DIR_NAME));

    // clear the cache, as in the below code, either we will also clear the snapshots, or we will
    // refill the file name cache again.
    if (ArrayUtils.isEmpty(snapshotDirs)) {
      // remove all the remembered snapshots because we don't have any left
      if (LOG.isDebugEnabled() && this.snapshots.size() > 0) {
        LOG.debug("No snapshots on-disk, clear cache");
      }
      this.snapshots = ImmutableMap.of();
      this.cache = ImmutableSet.of();
      return;
    }

    ImmutableSet.Builder<String> cacheBuilder = ImmutableSet.builder();
    ImmutableMap.Builder<String, SnapshotDirectoryInfo> snapshotsBuilder = ImmutableMap.builder();
    // iterate over all the cached snapshots and see if we need to update some, it is not an
    // expensive operation if we do not reload the manifest of snapshots.
    for (FileStatus snapshotDir : snapshotDirs) {
      String name = snapshotDir.getPath().getName();
      SnapshotDirectoryInfo files = snapshots.get(name);
      // if we don't know about the snapshot or its been modified, we need to update the
      // files the latter could occur where I create a snapshot, then delete it, and then make a
      // new snapshot with the same name. We will need to update the cache the information from
      // that new snapshot, even though it has the same name as the files referenced have
      // probably changed.
      if (files == null || files.hasBeenModified(snapshotDir.getModificationTime())) {
        Collection<String> storedFiles =
          fileInspector.filesUnderSnapshot(fs, snapshotDir.getPath());
        files = new SnapshotDirectoryInfo(snapshotDir.getModificationTime(), storedFiles);
      }
      // add all the files to cache
      cacheBuilder.addAll(files.getFiles());
      snapshotsBuilder.put(name, files);
    }
    // set the snapshots we are tracking
    this.snapshots = snapshotsBuilder.build();
    this.cache = cacheBuilder.build();
  }
Check to see if any of the passed file names is contained in any of the snapshots. First checks
an in-memory cache of the files to keep. If its not in the cache, then the cache is refreshed
and the cache checked again for that file. This ensures that we never return files that exist.
<p>
Note this may lead to periodic false positives for the file being referenced. Periodically, the
cache is refreshed even if there are no requests to ensure that the false negatives get removed
eventually. For instance, suppose you have a file in the snapshot and it gets loaded into the
cache. Then at some point later that snapshot is deleted. If the cache has not been refreshed
at that point, cache will still think the file system contains that file and return
<tt>true</tt>, even if it is no longer present (false positive). However, if the file never was
on the filesystem, we will never find it and always return <tt>false</tt>.
@param files file to check
@return <tt>unReferencedFiles</tt> the collection of files that do not have snapshot references
@throws IOException if there is an unexpected error reaching the filesystem.
hbase-server/src/main/java/org/apache/hadoop/hbase/master/snapshot/SnapshotFileCache.java