Index: src/main/java/org/apache/hadoop/hbase/regionserver/HRegion.java =================================================================== --- src/main/java/org/apache/hadoop/hbase/regionserver/HRegion.java (revision 1544911) +++ src/main/java/org/apache/hadoop/hbase/regionserver/HRegion.java (working copy) @@ -3845,7 +3845,7 @@ for (Map.Entry> entry : scan.getFamilyMap().entrySet()) { Store store = stores.get(entry.getKey()); - KeyValueScanner scanner = store.getScanner(scan, entry.getValue()); + KeyValueScanner scanner = store.getScanner(scan, entry.getValue(), this); if (this.filter == null || !scan.doLoadColumnFamiliesOnDemand() || FilterBase.isFamilyEssential(this.filter, entry.getKey())) { scanners.add(scanner); Index: src/main/java/org/apache/hadoop/hbase/regionserver/KeyValueScanner.java =================================================================== --- src/main/java/org/apache/hadoop/hbase/regionserver/KeyValueScanner.java (revision 1544911) +++ src/main/java/org/apache/hadoop/hbase/regionserver/KeyValueScanner.java (working copy) @@ -120,5 +120,11 @@ * @return true if this is a file scanner. Otherwise a memory scanner is * assumed. */ - public boolean isFileScanner(); + public boolean isFileScanner(); + + /** + * Set the object to lock when the scanners readers (if any) are updated + * @param obj + */ + public void setSyncObject(Object obj); } Index: src/main/java/org/apache/hadoop/hbase/regionserver/NonLazyKeyValueScanner.java =================================================================== --- src/main/java/org/apache/hadoop/hbase/regionserver/NonLazyKeyValueScanner.java (revision 1544911) +++ src/main/java/org/apache/hadoop/hbase/regionserver/NonLazyKeyValueScanner.java (working copy) @@ -66,4 +66,7 @@ // Not a file by default. return false; } + + @Override + public void setSyncObject(Object obj) {} } Index: src/main/java/org/apache/hadoop/hbase/regionserver/Store.java =================================================================== --- src/main/java/org/apache/hadoop/hbase/regionserver/Store.java (revision 1544911) +++ src/main/java/org/apache/hadoop/hbase/regionserver/Store.java (working copy) @@ -2202,8 +2202,8 @@ * are not in a compaction. * @throws IOException */ - public KeyValueScanner getScanner(Scan scan, - final NavigableSet targetCols) throws IOException { + public KeyValueScanner getScanner(Scan scan, final NavigableSet targetCols, + Object syncObjec) throws IOException { lock.readLock().lock(); try { KeyValueScanner scanner = null; @@ -2213,6 +2213,7 @@ if (scanner == null) { scanner = new StoreScanner(this, getScanInfo(), scan, targetCols); } + scanner.setSyncObject(syncObjec); return scanner; } finally { lock.readLock().unlock(); Index: src/main/java/org/apache/hadoop/hbase/regionserver/StoreFileScanner.java =================================================================== --- src/main/java/org/apache/hadoop/hbase/regionserver/StoreFileScanner.java (revision 1544911) +++ src/main/java/org/apache/hadoop/hbase/regionserver/StoreFileScanner.java (working copy) @@ -378,4 +378,6 @@ return reader.passesTimerangeFilter(scan, oldestUnexpiredTS) && reader.passesKeyRangeFilter(scan) && reader.passesBloomFilter(scan, columns); } + @Override + public void setSyncObject(Object Obj) {} } Index: src/main/java/org/apache/hadoop/hbase/regionserver/StoreScanner.java =================================================================== --- src/main/java/org/apache/hadoop/hbase/regionserver/StoreScanner.java (revision 1544911) +++ src/main/java/org/apache/hadoop/hbase/regionserver/StoreScanner.java (working copy) @@ -63,6 +63,8 @@ private final long oldestUnexpiredTS; private final int minVersions; + private Object syncObject; + /** We don't ever expect to change this, the constant is just for clarity. */ static final boolean LAZY_SEEK_ENABLED_BY_DEFAULT = true; @@ -267,7 +269,7 @@ } @Override - public synchronized KeyValue peek() { + public KeyValue peek() { if (this.heap == null) { return this.lastTop; } @@ -281,7 +283,7 @@ } @Override - public synchronized void close() { + public void close() { if (this.closing) return; this.closing = true; // under test, we dont have a this.store @@ -294,7 +296,7 @@ } @Override - public synchronized boolean seek(KeyValue key) throws IOException { + public boolean seek(KeyValue key) throws IOException { if (this.heap == null) { List scanners = getScannersNoCompaction(); @@ -312,7 +314,7 @@ * @return true if there are more rows, false if scanner is done */ @Override - public synchronized boolean next(List outResult, int limit) throws IOException { + public boolean next(List outResult, int limit) throws IOException { return next(outResult, limit, null); } @@ -323,7 +325,7 @@ * @return true if there are more rows, false if scanner is done */ @Override - public synchronized boolean next(List outResult, int limit, + public boolean next(List outResult, int limit, String metric) throws IOException { if (checkReseek()) { @@ -450,38 +452,40 @@ } @Override - public synchronized boolean next(List outResult) throws IOException { + public boolean next(List outResult) throws IOException { return next(outResult, -1, null); } @Override - public synchronized boolean next(List outResult, String metric) + public boolean next(List outResult, String metric) throws IOException { return next(outResult, -1, metric); } // Implementation of ChangedReadersObserver @Override - public synchronized void updateReaders() throws IOException { - if (this.closing) return; - - // All public synchronized API calls will call 'checkReseek' which will cause - // the scanner stack to reseek if this.heap==null && this.lastTop != null. - // But if two calls to updateReaders() happen without a 'next' or 'peek' then we - // will end up calling this.peek() which would cause a reseek in the middle of a updateReaders - // which is NOT what we want, not to mention could cause an NPE. So we early out here. - if (this.heap == null) return; - - // this could be null. - this.lastTop = this.peek(); - - //DebugPrint.println("SS updateReaders, topKey = " + lastTop); - - // close scanners to old obsolete Store files - this.heap.close(); // bubble thru and close all scanners. - this.heap = null; // the re-seeks could be slow (access HDFS) free up memory ASAP - - // Let the next() call handle re-creating and seeking + public void updateReaders() throws IOException { + synchronized(syncObject) { + if (this.closing) return; + + // All public synchronized API calls will call 'checkReseek' which will cause + // the scanner stack to reseek if this.heap==null && this.lastTop != null. + // But if two calls to updateReaders() happen without a 'next' or 'peek' then we + // will end up calling this.peek() which would cause a reseek in the middle of a updateReaders + // which is NOT what we want, not to mention could cause an NPE. So we early out here. + if (this.heap == null) return; + + // this could be null. + this.lastTop = this.peek(); + + //DebugPrint.println("SS updateReaders, topKey = " + lastTop); + + // close scanners to old obsolete Store files + this.heap.close(); // bubble thru and close all scanners. + this.heap = null; // the re-seeks could be slow (access HDFS) free up memory ASAP + + // Let the next() call handle re-creating and seeking + } } /** @@ -539,7 +543,7 @@ } @Override - public synchronized boolean reseek(KeyValue kv) throws IOException { + public boolean reseek(KeyValue kv) throws IOException { //Heap will not be null, if this is called from next() which. //If called from RegionScanner.reseek(...) make sure the scanner //stack is reset if needed. @@ -573,5 +577,9 @@ static void enableLazySeekGlobally(boolean enable) { lazySeekEnabledGlobally = enable; } + @Override + public void setSyncObject(Object obj) { + this.syncObject = obj; + } } Index: src/test/java/org/apache/hadoop/hbase/HBaseTestingUtility.java =================================================================== --- src/test/java/org/apache/hadoop/hbase/HBaseTestingUtility.java (revision 1544911) +++ src/test/java/org/apache/hadoop/hbase/HBaseTestingUtility.java (working copy) @@ -1934,7 +1934,7 @@ MultiVersionConsistencyControl.resetThreadReadPoint(); Scan scan = new Scan(get); InternalScanner scanner = (InternalScanner) store.getScanner(scan, - scan.getFamilyMap().get(store.getFamily().getName())); + scan.getFamilyMap().get(store.getFamily().getName()), null); List result = new ArrayList(); scanner.next(result);