private void finishRemainingHashRanges(Context context)
      throws IOException, InterruptedException {
      TableSplit split = (TableSplit) context.getInputSplit();
      byte[] splitEndRow = split.getEndRow();
      boolean reachedEndOfTable = HashTable.isTableEndRow(splitEndRow);

      // if there are more hash batches that begin before the end of this split move to them
      while (
        nextSourceKey != null && (nextSourceKey.compareTo(splitEndRow) < 0 || reachedEndOfTable)
      ) {
        moveToNextBatch(context);
      }

      if (targetHasher.isBatchStarted()) {
        // need to complete the final open hash batch

        if (
          (nextSourceKey != null && nextSourceKey.compareTo(splitEndRow) > 0)
            || (nextSourceKey == null && !Bytes.equals(splitEndRow, sourceTableHash.stopRow))
        ) {
          // the open hash range continues past the end of this region
          // add a scan to complete the current hash range
          Scan scan = sourceTableHash.initScan();
          scan.withStartRow(splitEndRow);
          if (nextSourceKey == null) {
            scan.withStopRow(sourceTableHash.stopRow);
          } else {
            scan.withStopRow(nextSourceKey.copyBytes());
          }

          ResultScanner targetScanner = null;
          try {
            targetScanner = targetTable.getScanner(scan);
            for (Result row : targetScanner) {
              targetHasher.hashResult(row);
            }
          } finally {
            if (targetScanner != null) {
              targetScanner.close();
            }
          }
        } // else current batch ends exactly at split end row

        finishBatchAndCompareHashes(context);
      }
    }
Compare families, qualifiers, and timestamps of the given Cells. They are assumed to be of
the same row. Nulls are after non-nulls.
hbase-mapreduce/src/main/java/org/apache/hadoop/hbase/mapreduce/SyncTable.java