public boolean nextKeyValue() throws IOException, InterruptedException {
    if (key == null) {
      key = new ImmutableBytesWritable();
    }
    if (value == null) {
      value = new Result();
    }
    try {
      try {
        value = this.scanner.next();
        if (value != null && value.isStale()) {
          numStale++;
        }
        if (logScannerActivity) {
          rowcount++;
          if (rowcount >= logPerRowCount) {
            long now = EnvironmentEdgeManager.currentTime();
            LOG.info("Mapper took {}ms to process {} rows", (now - timestamp), rowcount);
            timestamp = now;
            rowcount = 0;
          }
        }
      } catch (IOException e) {
        // do not retry if the exception tells us not to do so
        if (e instanceof DoNotRetryIOException) {
          updateCounters();
          throw e;
        }
        // try to handle all other IOExceptions by restarting
        // the scanner, if the second call fails, it will be rethrown
        LOG.info("recovered from " + StringUtils.stringifyException(e));
        if (lastSuccessfulRow == null) {
          LOG.warn("We are restarting the first next() invocation,"
            + " if your mapper has restarted a few other times like this"
            + " then you should consider killing this job and investigate"
            + " why it's taking so long.");
        }
        if (lastSuccessfulRow == null) {
          restart(scan.getStartRow());
        } else {
          restart(lastSuccessfulRow);
          scanner.next(); // skip presumed already mapped row
        }
        value = scanner.next();
        if (value != null && value.isStale()) {
          numStale++;
        }
        numRestarts++;
      }

      if (value != null && value.size() > 0) {
        key.set(value.getRow());
        lastSuccessfulRow = key.get();
        return true;
      }

      // Need handle cursor result
      if (value != null && value.isCursor()) {
        key.set(value.getCursor().getRow());
        lastSuccessfulRow = key.get();
        return true;
      }

      updateCounters();
      return false;
    } catch (IOException ioe) {
      updateCounters();
      if (logScannerActivity) {
        long now = EnvironmentEdgeManager.currentTime();
        LOG.info("Mapper took {}ms to process {} rows", (now - timestamp), rowcount);
        LOG.info(ioe.toString(), ioe);
        String lastRow =
          lastSuccessfulRow == null ? "null" : Bytes.toStringBinary(lastSuccessfulRow);
        LOG.info("lastSuccessfulRow=" + lastRow);
      }
      throw ioe;
    }
  }
Positions the record reader to the next record.
@return <code>true</code> if there was another record.
@throws IOException          When reading the record failed.
@throws InterruptedException When the job was aborted.
hbase-mapreduce/src/main/java/org/apache/hadoop/hbase/mapreduce/TableRecordReaderImpl.java