@Override
    public void reduce(Text key, Iterable<ImmutableBytesWritable> rows, Context context)
      throws IOException, InterruptedException {
      final Configuration conf = context.getConfiguration();
      final String file = key.toString();
      // active mob area
      if (mob.getFileSystem(conf).exists(new Path(mob, file))) {
        LOG.debug("Found file '{}' in mob area", file);
        context.write(OK_MOB_DIR, key);
        // archive area - is there an hlink back reference (from a snapshot from same table)
      } else if (archive.getFileSystem(conf).exists(new Path(archive, file))) {

        Path backRefDir = HFileLink.getBackReferencesDir(archive, file);
        try {
          FileStatus[] backRefs = CommonFSUtils.listStatus(archive.getFileSystem(conf), backRefDir);
          if (backRefs != null) {
            boolean found = false;
            for (FileStatus backRef : backRefs) {
              Pair<TableName, String> refParts =
                HFileLink.parseBackReferenceName(backRef.getPath().getName());
              if (table.equals(refParts.getFirst()) && mobRegion.equals(refParts.getSecond())) {
                Path hlinkPath =
                  HFileLink.getHFileFromBackReference(MobUtils.getMobHome(conf), backRef.getPath());
                if (hlinkPath.getFileSystem(conf).exists(hlinkPath)) {
                  found = true;
                } else {
                  LOG.warn(
                    "Found file '{}' in archive area with a back reference to the mob area "
                      + "for our table, but the mob area does not have a corresponding hfilelink.",
                    file);
                }
              }
            }
            if (found) {
              LOG.debug("Found file '{}' in archive area. has proper hlink back references to "
                + "suggest it is from a restored snapshot for this table.", file);
              context.write(OK_HLINK_RESTORE, key);
            } else {
              LOG.warn("Found file '{}' in archive area, but the hlink back references do not "
                + "properly point to the mob area for our table.", file);
              context.write(INCONSISTENT_ARCHIVE_BAD_LINK, encodeRows(context, key, rows));
            }
          } else {
            LOG.warn("Found file '{}' in archive area, but there are no hlinks pointing to it. Not "
              + "yet used snapshot or an error.", file);
            context.write(INCONSISTENT_ARCHIVE_STALE, encodeRows(context, key, rows));
          }
        } catch (IOException e) {
          LOG.warn("Found file '{}' in archive area, but got an error while checking "
            + "on back references.", file, e);
          context.write(INCONSISTENT_ARCHIVE_IOE, encodeRows(context, key, rows));
        }

      } else {
        // check for an hlink in the active mob area (from a snapshot of a different table)
        try {
          /**
           * we are doing this ourselves instead of using FSUtils.getReferenceFilePaths because we
           * know the mob region never splits, so we can only have HFileLink references and looking
           * for just them is cheaper then listing everything. This glob should match the naming
           * convention for HFileLinks to our referenced hfile. As simplified explanation those file
           * names look like "table=region-hfile". For details see the
           * {@link HFileLink#createHFileLinkName HFileLink implementation}.
           */
          FileStatus[] hlinks = mob.getFileSystem(conf).globStatus(new Path(mob + "/*=*-" + file));
          if (hlinks != null && hlinks.length != 0) {
            if (hlinks.length != 1) {
              LOG.warn("Found file '{}' as hfilelinks in the mob area, but there are more than "
                + "one: {}", file, Arrays.deepToString(hlinks));
            }
            HFileLink found = null;
            for (FileStatus hlink : hlinks) {
              HFileLink tmp = HFileLink.buildFromHFileLinkPattern(conf, hlink.getPath());
              if (tmp.exists(archive.getFileSystem(conf))) {
                found = tmp;
                break;
              } else {
                LOG.debug("Target file does not exist for ref {}", tmp);
              }
            }
            if (found != null) {
              LOG.debug("Found file '{}' as a ref in the mob area: {}", file, found);
              context.write(OK_HLINK_CLONE, key);
            } else {
              LOG.warn("Found file '{}' as ref(s) in the mob area but they do not point to an hfile"
                + " that exists.", file);
              context.write(DATALOSS_HLINK_DANGLING, encodeRows(context, key, rows));
            }
          } else {
            LOG.error("Could not find referenced file '{}'. See the docs on this tool.", file);
            LOG.debug("Note that we don't have the server-side tag from the mob cells that says "
              + "what table the reference is originally from. So if the HFileLink in this table "
              + "is missing but the referenced file is still in the table from that tag, then "
              + "lookups of these impacted rows will work. Do a scan of the reference details "
              + "of the cell for the hfile name and then check the entire hbase install if this "
              + "table was made from a snapshot of another table. see the ref guide section on "
              + "mob for details.");
            context.write(DATALOSS_MISSING, encodeRows(context, key, rows));
          }
        } catch (IOException e) {
          LOG.error(
            "Exception while checking mob area of our table for HFileLinks that point to {}", file,
            e);
          context.write(DATALOSS_MISSING_IOE, encodeRows(context, key, rows));
        }
      }
    }
Scans a given table + CF for all mob reference cells to get the list of backing mob files. For
each referenced file we attempt to verify that said file is on the FileSystem in a place that the
MOB system will look when attempting to resolve the actual value.
<p/>
The job includes counters that can help provide a rough sketch of the mob data.

<pre>
Map-Reduce Framework
        Map input records=10000
...
        Reduce output records=99
...
CELLS PER ROW
        Number of rows with 1s of cells per row=10000
MOB
        NUM_CELLS=52364
PROBLEM
        Affected rows=338
        Problem MOB files=2
ROWS WITH PROBLEMS PER FILE
        Number of HFiles with 100s of affected rows=2
SIZES OF CELLS
        Number of cells with size in the 10,000s of bytes=627
        Number of cells with size in the 100,000s of bytes=51392
        Number of cells with size in the 1,000,000s of bytes=345
SIZES OF ROWS
        Number of rows with total size in the 100,000s of bytes=6838
        Number of rows with total size in the 1,000,000s of bytes=3162
</pre>
<ol>
<li>Map-Reduce Framework:Map input records - the number of rows with mob references</li>
<li>Map-Reduce Framework:Reduce output records - the number of unique hfiles referenced</li>
<li>MOB:NUM_CELLS - the total number of mob reference cells</li>
<li>PROBLEM:Affected rows - the number of rows that reference hfiles with an issue</li>
<li>PROBLEM:Problem MOB files - the number of unique hfiles that have an issue</li>
<li>CELLS PER ROW: - this counter group gives a histogram of the order of magnitude of the number
of cells in a given row by grouping by the number of digits used in each count. This allows us to
see more about the distribution of cells than what we can determine with just the cell count and
the row count. In this particular example we can see that all of our rows have somewhere between
1 - 9 cells.</li>
<li>ROWS WITH PROBLEMS PER FILE: - this counter group gives a histogram of the order of magnitude
of the number of rows in each of the hfiles with a problem. e.g. in the example there are 2
hfiles and they each have the same order of magnitude number of rows, specifically between 100
and 999.</li>
<li>SIZES OF CELLS: - this counter group gives a histogram of the order of magnitude of the size
of mob values according to our reference cells. e.g. in the example above we have cell sizes that
are all between 10,000 bytes and 9,999,999 bytes. From this histogram we can also see that _most_
cells are 100,000 - 999,000 bytes and the smaller and bigger ones are outliers making up less
than 2% of mob cells.</li>
<li>SIZES OF ROWS: - this counter group gives a histogram of the order of magnitude of the size
of mob values across each row according to our reference cells. In the example above we have rows
that are are between 100,000 bytes and 9,999,999 bytes. We can also see that about 2/3rd of our
rows are 100,000 - 999,999 bytes.</li>
</ol>
Generates a report that gives one file status per line, with tabs dividing fields.

<pre>
RESULT OF LOOKUP	FILE REF	comma seperated, base64 encoded rows when there's a problem
</pre>

e.g.

<pre>
MOB DIR	09c576e28a65ed2ead0004d192ffaa382019110184b30a1c7e034573bf8580aef8393402
MISSING FILE    28e252d7f013973174750d483d358fa020191101f73536e7133f4cd3ab1065edf588d509        MmJiMjMyYzBiMTNjNzc0OTY1ZWY4NTU4ZjBmYmQ2MTUtNTIz,MmEzOGE0YTkzMTZjNDllNWE4MzM1MTdjNDVkMzEwNzAtODg=
</pre>

Possible results are listed; the first three indicate things are working properly.
<ol>
<li>MOB DIR - the reference is in the normal MOB area for the given table and CF</li>
<li>HLINK TO ARCHIVE FOR SAME TABLE - the reference is present in the archive area for this table
and CF</li>
<li>HLINK TO ARCHIVE FOR OTHER TABLE - the reference is present in a different table and CF,
either in the MOB or archive areas (e.g. from a snapshot restore or clone)</li>
<li>ARCHIVE WITH HLINK BUT NOT FROM OUR TABLE - the reference is currently present in the archive
area for this table and CF, but it is kept there because a _different_ table has a reference to
it (e.g. from a snapshot clone). If these other tables are removed then the file will likely be
deleted unless there is a snapshot also referencing it.</li>
<li>ARCHIVE BUT NO HLINKS - the reference is currently present in the archive for this table and
CF, but there are no references present to prevent its removal. Unless it is newer than the
general TTL (default 5 minutes) or referenced in a snapshot it will be subject to cleaning.</li>
<li>ARCHIVE BUT FAILURE WHILE CHECKING HLINKS - Check the job logs to see why things failed while
looking for why this file is being kept around.</li>
<li>MISSING FILE - We couldn't find the reference on the FileSystem. Either there is dataloss due
to a bug in the MOB storage system or the MOB storage is damaged but in an edge case that allows
it to work for now. You can verify which by doing a raw reference scan to get the referenced
hfile and check the underlying filesystem. See the ref guide section on mob for details.</li>
<li>HLINK BUT POINT TO MISSING FILE - There is a pointer in our mob area for this table and CF to
a file elsewhere on the FileSystem, however the file it points to no longer exists.</li>
<li>MISSING FILE BUT FAILURE WHILE CHECKING HLINKS - We could not find the referenced file,
however you should check the job logs to see why we couldn't check to see if there is a pointer
to the referenced file in our archive or another table's archive or mob area.</li>
</ol>
hbase-mapreduce/src/main/java/org/apache/hadoop/hbase/mob/mapreduce/MobRefReporter.java