protected KeyValueScanner pollRealKV() throws IOException {
    KeyValueScanner kvScanner = heap.poll();
    if (kvScanner == null) {
      return null;
    }

    while (kvScanner != null && !kvScanner.realSeekDone()) {
      if (kvScanner.peek() != null) {
        try {
          kvScanner.enforceSeek();
        } catch (IOException ioe) {
          // Add the item to delayed close set in case it is leak from close
          this.scannersForDelayedClose.add(kvScanner);
          throw ioe;
        }
        Cell curKV = kvScanner.peek();
        if (curKV != null) {
          KeyValueScanner nextEarliestScanner = heap.peek();
          if (nextEarliestScanner == null) {
            // The heap is empty. Return the only possible scanner.
            return kvScanner;
          }

          // Compare the current scanner to the next scanner. We try to avoid
          // putting the current one back into the heap if possible.
          Cell nextKV = nextEarliestScanner.peek();
          if (nextKV == null || comparator.compare(curKV, nextKV) < 0) {
            // We already have the scanner with the earliest KV, so return it.
            return kvScanner;
          }

          // Otherwise, put the scanner back into the heap and let it compete
          // against all other scanners (both those that have done a "real
          // seek" and a "lazy seek").
          heap.add(kvScanner);
        } else {
          // Close the scanner because we did a real seek and found out there
          // are no more KVs.
          this.scannersForDelayedClose.add(kvScanner);
        }
      } else {
        // Close the scanner because it has already run out of KVs even before
        // we had to do a real seek on it.
        this.scannersForDelayedClose.add(kvScanner);
      }
      kvScanner = heap.poll();
    }

    return kvScanner;
  }
Fetches the top sub-scanner from the priority queue, ensuring that a real seek has been done on
it. Works by fetching the top sub-scanner, and if it has not done a real seek, making it do so
(which will modify its top KV), putting it back, and repeating this until success. Relies on
the fact that on a lazy seek we set the current key of a StoreFileScanner to a KV that is not
greater than the real next KV to be read from that file, so the scanner that bubbles up to the
top of the heap will have global next KV in this scanner heap if (1) it has done a real seek
and (2) its KV is the top among all top KVs (some of which are fake) in the scanner heap.
hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/KeyValueHeap.java