Index: src/main/java/org/apache/hadoop/hbase/regionserver/PeekChangedException.java =================================================================== --- src/main/java/org/apache/hadoop/hbase/regionserver/PeekChangedException.java (revision 0) +++ src/main/java/org/apache/hadoop/hbase/regionserver/PeekChangedException.java (revision 0) @@ -0,0 +1,27 @@ +package org.apache.hadoop.hbase.regionserver; + +import java.io.IOException; + +/** + * Thrown when storescanner.checkReseek() find storescanner.peek() is changed + * after majorCompaction + */ +public class PeekChangedException extends IOException { + + private static final long serialVersionUID = 4802308828574427604L; + + /** constructor */ + public PeekChangedException() { + super(); + } + + /** + * Constructor + * + * @param s message + */ + public PeekChangedException(String s) { + super(s); + } + +} Index: src/main/java/org/apache/hadoop/hbase/regionserver/StoreScanner.java =================================================================== --- src/main/java/org/apache/hadoop/hbase/regionserver/StoreScanner.java (revision 1227045) +++ src/main/java/org/apache/hadoop/hbase/regionserver/StoreScanner.java (working copy) @@ -451,6 +451,14 @@ private void checkReseek() throws IOException { if (this.heap == null && this.lastTop != null) { resetScannerStack(this.lastTop); + if (this.heap.peek() == null + || store.comparator.compare(this.lastTop, this.heap.peek()) != 0) { + this.lastTop = null; + LOG.debug("Storescanner.peek() is changed where before = " + + this.lastTop.toString() + ",and after = " + + (this.heap.peek() == null ? "null" : this.heap.peek().toString())); + throw new PeekChangedException(); + } this.lastTop = null; // gone! } // else dont need to reseek Index: src/main/java/org/apache/hadoop/hbase/regionserver/KeyValueHeap.java =================================================================== --- src/main/java/org/apache/hadoop/hbase/regionserver/KeyValueHeap.java (revision 1227045) +++ src/main/java/org/apache/hadoop/hbase/regionserver/KeyValueHeap.java (working copy) @@ -20,14 +20,14 @@ package org.apache.hadoop.hbase.regionserver; -import org.apache.hadoop.hbase.KeyValue; -import org.apache.hadoop.hbase.KeyValue.KVComparator; - import java.io.IOException; import java.util.Comparator; import java.util.List; import java.util.PriorityQueue; +import org.apache.hadoop.hbase.KeyValue; +import org.apache.hadoop.hbase.KeyValue.KVComparator; + /** * Implements a heap merge across any number of KeyValueScanners. *

@@ -124,22 +124,32 @@ return false; } InternalScanner currentAsInternal = (InternalScanner)this.current; - boolean mayContainsMoreRows = currentAsInternal.next(result, limit); - KeyValue pee = this.current.peek(); - /* - * By definition, any InternalScanner must return false only when it has no - * further rows to be fetched. So, we can close a scanner if it returns - * false. All existing implementations seem to be fine with this. It is much - * more efficient to close scanners which are not needed than keep them in - * the heap. This is also required for certain optimizations. - */ - if (pee == null || !mayContainsMoreRows) { - this.current.close(); - } else { - this.heap.add(this.current); + try { + boolean mayContainsMoreRows = currentAsInternal.next(result, limit); + KeyValue pee = this.current.peek(); + /* + * By definition, any InternalScanner must return false only when it has + * no further rows to be fetched. So, we can close a scanner if it returns + * false. All existing implementations seem to be fine with this. It is + * much more efficient to close scanners which are not needed than keep + * them in the heap. This is also required for certain optimizations. + */ + if (pee == null || !mayContainsMoreRows) { + this.current.close(); + } else { + this.heap.add(this.current); + } + this.current = pollRealKV(); + return (this.current != null); + } catch (PeekChangedException e) { + if (this.current.peek() == null) { + this.current.close(); + } else { + this.heap.add(this.current); + } + this.current = this.heap.poll(); + return next(result, limit); } - this.current = pollRealKV(); - return (this.current != null); } /**