private void syncRange(Context context, ImmutableBytesWritable startRow,
      ImmutableBytesWritable stopRow) throws IOException, InterruptedException {
      Scan scan = sourceTableHash.initScan();
      scan.withStartRow(startRow.copyBytes());
      scan.withStopRow(stopRow.copyBytes());

      ResultScanner sourceScanner = sourceTable.getScanner(scan);
      CellScanner sourceCells = new CellScanner(sourceScanner.iterator());

      ResultScanner targetScanner = targetTable.getScanner(new Scan(scan));
      CellScanner targetCells = new CellScanner(targetScanner.iterator());

      boolean rangeMatched = true;
      byte[] nextSourceRow = sourceCells.nextRow();
      byte[] nextTargetRow = targetCells.nextRow();
      while (nextSourceRow != null || nextTargetRow != null) {
        boolean rowMatched;
        int rowComparison = compareRowKeys(nextSourceRow, nextTargetRow);
        if (rowComparison < 0) {
          if (LOG.isDebugEnabled()) {
            LOG.debug("Target missing row: " + Bytes.toString(nextSourceRow));
          }
          context.getCounter(Counter.TARGETMISSINGROWS).increment(1);

          rowMatched = syncRowCells(context, nextSourceRow, sourceCells, EMPTY_CELL_SCANNER);
          nextSourceRow = sourceCells.nextRow(); // advance only source to next row
        } else if (rowComparison > 0) {
          if (LOG.isDebugEnabled()) {
            LOG.debug("Source missing row: " + Bytes.toString(nextTargetRow));
          }
          context.getCounter(Counter.SOURCEMISSINGROWS).increment(1);

          rowMatched = syncRowCells(context, nextTargetRow, EMPTY_CELL_SCANNER, targetCells);
          nextTargetRow = targetCells.nextRow(); // advance only target to next row
        } else {
          // current row is the same on both sides, compare cell by cell
          rowMatched = syncRowCells(context, nextSourceRow, sourceCells, targetCells);
          nextSourceRow = sourceCells.nextRow();
          nextTargetRow = targetCells.nextRow();
        }

        if (!rowMatched) {
          rangeMatched = false;
        }
      }

      sourceScanner.close();
      targetScanner.close();

      context.getCounter(rangeMatched ? Counter.RANGESMATCHED : Counter.RANGESNOTMATCHED)
        .increment(1);
    }
Rescan the given range directly from the source and target tables. Count and log differences,
and if this is not a dry run, output Puts and Deletes to make the target table match the
source table for this range
hbase-mapreduce/src/main/java/org/apache/hadoop/hbase/mapreduce/SyncTable.java