diff --git a/CHANGES.txt b/CHANGES.txt index b32cd1f10a4..7002d4a9de6 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -78,6 +78,7 @@ Release 0.21.0 - Unreleased HBASE-1777 column length is not checked before saved to memstore HBASE-1925 IllegalAccessError: Has not been initialized (getMaxSequenceId) HBASE-1929 If hbase-default.xml is not in CP, zk session timeout is 10 secs! + HBASE-1927 Scanners not closed properly in certain circumstances IMPROVEMENTS HBASE-1760 Cleanup TODOs in HTable diff --git a/src/java/org/apache/hadoop/hbase/regionserver/KeyValueHeap.java b/src/java/org/apache/hadoop/hbase/regionserver/KeyValueHeap.java index c7932307439..63f94afa068 100644 --- a/src/java/org/apache/hadoop/hbase/regionserver/KeyValueHeap.java +++ b/src/java/org/apache/hadoop/hbase/regionserver/KeyValueHeap.java @@ -46,7 +46,8 @@ public class KeyValueHeap implements KeyValueScanner, InternalScanner { private KVScannerComparator comparator; /** - * Constructor + * Constructor. This KeyValueHeap will handle closing of passed in + * KeyValueScanners. * @param scanners * @param comparator */ @@ -57,6 +58,8 @@ public class KeyValueHeap implements KeyValueScanner, InternalScanner { for (KeyValueScanner scanner : scanners) { if (scanner.peek() != null) { this.heap.add(scanner); + } else { + scanner.close(); } } this.current = heap.poll(); diff --git a/src/test/org/apache/hadoop/hbase/regionserver/TestKeyValueHeap.java b/src/test/org/apache/hadoop/hbase/regionserver/TestKeyValueHeap.java index 8312f0ca104..74d19df9376 100644 --- a/src/test/org/apache/hadoop/hbase/regionserver/TestKeyValueHeap.java +++ b/src/test/org/apache/hadoop/hbase/regionserver/TestKeyValueHeap.java @@ -154,9 +154,46 @@ implements HConstants { } + public void testScannerLeak() { + // Test for unclosed scanners (HBASE-1927) + + List l1 = new ArrayList(); + l1.add(new KeyValue(row1, fam1, col5, data)); + l1.add(new KeyValue(row2, fam1, col1, data)); + l1.add(new KeyValue(row2, fam1, col2, data)); + scanners.add(new Scanner(l1)); + + List l2 = new ArrayList(); + l2.add(new KeyValue(row1, fam1, col1, data)); + l2.add(new KeyValue(row1, fam1, col2, data)); + scanners.add(new Scanner(l2)); + + List l3 = new ArrayList(); + l3.add(new KeyValue(row1, fam1, col3, data)); + l3.add(new KeyValue(row1, fam1, col4, data)); + l3.add(new KeyValue(row1, fam2, col1, data)); + l3.add(new KeyValue(row1, fam2, col2, data)); + l3.add(new KeyValue(row2, fam1, col3, data)); + scanners.add(new Scanner(l3)); + + List l4 = new ArrayList(); + scanners.add(new Scanner(l4)); + + //Creating KeyValueHeap + KeyValueHeap kvh = + new KeyValueHeap(scanners.toArray(new Scanner[0]), KeyValue.COMPARATOR); + + while(kvh.next() != null); + + for(Scanner scanner : scanners) { + assertTrue(scanner.isClosed()); + } + } + private static class Scanner implements KeyValueScanner { private Iterator iter; private KeyValue current; + private boolean closed = false; public Scanner(List list) { Collections.sort(list, KeyValue.COMPARATOR); @@ -180,7 +217,13 @@ implements HConstants { return oldCurrent; } - public void close(){} + public void close(){ + closed = true; + } + + public boolean isClosed() { + return closed; + } public boolean seek(KeyValue seekKv) { while(iter.hasNext()){