View Javadoc

1   /*
2    *
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *     http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the License for the specific language governing permissions and
17   * limitations under the License.
18   */
19  
20  package org.apache.hadoop.hbase.regionserver;
21  
22  import static org.apache.hadoop.hbase.regionserver.KeyValueScanFixture.scanFixture;
23  
24  import java.io.IOException;
25  import java.util.ArrayList;
26  import java.util.Arrays;
27  import java.util.Collections;
28  import java.util.List;
29  import java.util.NavigableSet;
30  import java.util.TreeSet;
31  import java.util.concurrent.atomic.AtomicInteger;
32  
33  import org.apache.commons.logging.Log;
34  import org.apache.commons.logging.LogFactory;
35  import org.apache.hadoop.conf.Configuration;
36  import org.apache.hadoop.hbase.Cell;
37  import org.apache.hadoop.hbase.CellUtil;
38  import org.apache.hadoop.hbase.HBaseConfiguration;
39  import org.apache.hadoop.hbase.HConstants;
40  import org.apache.hadoop.hbase.KeepDeletedCells;
41  import org.apache.hadoop.hbase.KeyValue;
42  import org.apache.hadoop.hbase.KeyValue.KVComparator;
43  import org.apache.hadoop.hbase.KeyValueTestUtil;
44  import org.apache.hadoop.hbase.KeyValueUtil;
45  import org.apache.hadoop.hbase.client.Get;
46  import org.apache.hadoop.hbase.client.Scan;
47  import org.apache.hadoop.hbase.filter.ColumnCountGetFilter;
48  import org.apache.hadoop.hbase.testclassification.MediumTests;
49  import org.apache.hadoop.hbase.util.Bytes;
50  import org.apache.hadoop.hbase.util.EnvironmentEdge;
51  import org.apache.hadoop.hbase.util.EnvironmentEdgeManagerTestHelper;
52  import org.junit.experimental.categories.Category;
53  
54  import junit.framework.TestCase;
55  
56  // Can't be small as it plays with EnvironmentEdgeManager
57  @Category(MediumTests.class)
58  public class TestStoreScanner extends TestCase {
59    private static final Log LOG = LogFactory.getLog(TestStoreScanner.class);
60    private static final String CF_STR = "cf";
61    final static byte [] CF = Bytes.toBytes(CF_STR);
62    static Configuration CONF = HBaseConfiguration.create();
63    private ScanInfo scanInfo = new ScanInfo(CONF, CF, 0, Integer.MAX_VALUE,
64        Long.MAX_VALUE, KeepDeletedCells.FALSE, 0, KeyValue.COMPARATOR);
65    private ScanType scanType = ScanType.USER_SCAN;
66  
67    public void setUp() throws Exception {
68      super.setUp();
69    }
70  
71    /**
72     * From here on down, we have a bunch of defines and specific CELL_GRID of Cells. The
73     * CELL_GRID then has a Scanner that can fake out 'block' transitions. All this elaborate
74     * setup is for tests that ensure we don't overread, and that the {@link StoreScanner} is not
75     * overly enthusiastic.
76     */
77    private static final byte [] ZERO = new byte [] {'0'};
78    private static final byte [] ZERO_POINT_ZERO = new byte [] {'0', '.', '0'};
79    private static final byte [] ONE = new byte [] {'1'};
80    private static final byte [] TWO = new byte [] {'2'};
81    private static final byte [] TWO_POINT_TWO = new byte [] {'2', '.', '2'};
82    private static final byte [] THREE = new byte [] {'3'};
83    private static final byte [] FOUR = new byte [] {'4'};
84    private static final byte [] FIVE = new byte [] {'5'};
85    private static final byte [] VALUE = new byte [] {'v'};
86    private static final int CELL_GRID_BLOCK2_BOUNDARY = 4;
87    private static final int CELL_GRID_BLOCK3_BOUNDARY = 11;
88    private static final int CELL_GRID_BLOCK4_BOUNDARY = 15;
89    private static final int CELL_GRID_BLOCK5_BOUNDARY = 19;
90  
91    /**
92     * Five rows by four columns distinguished by column qualifier (column qualifier is one of the
93     * four rows... ONE, TWO, etc.). Exceptions are a weird row after TWO; it is TWO_POINT_TWO.
94     * And then row FOUR has five columns finishing w/ row FIVE having a single column.
95     * We will use this to test scan does the right thing as it
96     * we do Gets, StoreScanner#optimize, and what we do on (faked) block boundaries.
97     */
98    private static final KeyValue [] CELL_GRID = new KeyValue [] {
99      new KeyValue(ONE, CF, ONE, 1L, KeyValue.Type.Put, VALUE),
100     new KeyValue(ONE, CF, TWO, 1L, KeyValue.Type.Put, VALUE),
101     new KeyValue(ONE, CF, THREE, 1L, KeyValue.Type.Put, VALUE),
102     new KeyValue(ONE, CF, FOUR, 1L, KeyValue.Type.Put, VALUE),
103     // Offset 4 CELL_GRID_BLOCK2_BOUNDARY
104     new KeyValue(TWO, CF, ONE, 1L, KeyValue.Type.Put, VALUE),
105     new KeyValue(TWO, CF, TWO, 1L, KeyValue.Type.Put, VALUE),
106     new KeyValue(TWO, CF, THREE, 1L, KeyValue.Type.Put, VALUE),
107     new KeyValue(TWO, CF, FOUR, 1L, KeyValue.Type.Put, VALUE),
108     new KeyValue(TWO_POINT_TWO, CF, ZERO, 1L, KeyValue.Type.Put, VALUE),
109     new KeyValue(TWO_POINT_TWO, CF, ZERO_POINT_ZERO, 1L, KeyValue.Type.Put, VALUE),
110     new KeyValue(TWO_POINT_TWO, CF, FIVE, 1L, KeyValue.Type.Put, VALUE),
111     // Offset 11! CELL_GRID_BLOCK3_BOUNDARY
112     new KeyValue(THREE, CF, ONE, 1L, KeyValue.Type.Put, VALUE),
113     new KeyValue(THREE, CF, TWO, 1L, KeyValue.Type.Put, VALUE),
114     new KeyValue(THREE, CF, THREE, 1L, KeyValue.Type.Put, VALUE),
115     new KeyValue(THREE, CF, FOUR, 1L, KeyValue.Type.Put, VALUE),
116     // Offset 15 CELL_GRID_BLOCK4_BOUNDARY
117     new KeyValue(FOUR, CF, ONE, 1L, KeyValue.Type.Put, VALUE),
118     new KeyValue(FOUR, CF, TWO, 1L, KeyValue.Type.Put, VALUE),
119     new KeyValue(FOUR, CF, THREE, 1L, KeyValue.Type.Put, VALUE),
120     new KeyValue(FOUR, CF, FOUR, 1L, KeyValue.Type.Put, VALUE),
121     // Offset 19 CELL_GRID_BLOCK5_BOUNDARY
122     new KeyValue(FOUR, CF, FIVE, 1L, KeyValue.Type.Put, VALUE),
123     new KeyValue(FIVE, CF, ZERO, 1L, KeyValue.Type.Put, VALUE),
124   };
125 
126   private static class KeyValueHeapWithCount extends KeyValueHeap {
127 
128     final AtomicInteger count;
129 
130     public KeyValueHeapWithCount(List<? extends KeyValueScanner> scanners,
131         KVComparator comparator, AtomicInteger count) throws IOException {
132       super(scanners, comparator);
133       this.count = count;
134     }
135 
136     @Override
137     public Cell peek() {
138       this.count.incrementAndGet();
139       return super.peek();
140     }
141   }
142 
143   /**
144    * A StoreScanner for our CELL_GRID above. Fakes the block transitions. Does counts of
145    * calls to optimize and counts of when optimize actually did an optimize.
146    */
147   private static class CellGridStoreScanner extends StoreScanner {
148     // Count of how often optimize is called and of how often it does an optimize.
149     AtomicInteger count;
150     final AtomicInteger optimization = new AtomicInteger(0);
151 
152     CellGridStoreScanner(final Scan scan, ScanInfo scanInfo, ScanType scanType)
153     throws IOException {
154       super(scan, scanInfo, scanType, scan.getFamilyMap().get(CF),
155         Arrays.<KeyValueScanner>asList(
156           new KeyValueScanner[] {new KeyValueScanFixture(KeyValue.COMPARATOR, CELL_GRID)}));
157     }
158 
159     protected void resetKVHeap(List<? extends KeyValueScanner> scanners,
160         KVComparator comparator) throws IOException {
161       if (count == null) {
162         count = new AtomicInteger(0);
163       }
164       heap = new KeyValueHeapWithCount(scanners, comparator, count);
165     }
166 
167     protected boolean trySkipToNextRow(Cell cell) throws IOException {
168       boolean optimized = super.trySkipToNextRow(cell);
169       LOG.info("Cell=" + cell + ", nextIndex=" + CellUtil.toString(getNextIndexedKey(), false)
170           + ", optimized=" + optimized);
171       if (optimized) {
172         optimization.incrementAndGet();
173       }
174       return optimized;
175     }
176 
177     protected boolean trySkipToNextColumn(Cell cell) throws IOException {
178       boolean optimized = super.trySkipToNextColumn(cell);
179       LOG.info("Cell=" + cell + ", nextIndex=" + CellUtil.toString(getNextIndexedKey(), false)
180           + ", optimized=" + optimized);
181       if (optimized) {
182         optimization.incrementAndGet();
183       }
184       return optimized;
185     }
186 
187     @Override
188     public Cell getNextIndexedKey() {
189       // Fake block boundaries by having index of next block change as we go through scan.
190       return count.get() > CELL_GRID_BLOCK4_BOUNDARY?
191           KeyValueUtil.createFirstOnRow(CELL_GRID[CELL_GRID_BLOCK5_BOUNDARY].getRow()):
192             count.get() > CELL_GRID_BLOCK3_BOUNDARY?
193                 KeyValueUtil.createFirstOnRow(CELL_GRID[CELL_GRID_BLOCK4_BOUNDARY].getRow()):
194                   count.get() > CELL_GRID_BLOCK2_BOUNDARY?
195                       KeyValueUtil.createFirstOnRow(CELL_GRID[CELL_GRID_BLOCK3_BOUNDARY].getRow()):
196                         KeyValueUtil.createFirstOnRow(CELL_GRID[CELL_GRID_BLOCK2_BOUNDARY].getRow());
197     }
198   };
199 
200   private static final int CELL_WITH_VERSIONS_BLOCK2_BOUNDARY = 4;
201 
202   private static final KeyValue [] CELL_WITH_VERSIONS = new KeyValue [] {
203     new KeyValue(ONE, CF, ONE, 2L, KeyValue.Type.Put, VALUE),
204     new KeyValue(ONE, CF, ONE, 1L, KeyValue.Type.Put, VALUE),
205     new KeyValue(ONE, CF, TWO, 2L, KeyValue.Type.Put, VALUE),
206     new KeyValue(ONE, CF, TWO, 1L, KeyValue.Type.Put, VALUE),
207     // Offset 4 CELL_WITH_VERSIONS_BLOCK2_BOUNDARY
208     new KeyValue(TWO, CF, ONE, 1L, KeyValue.Type.Put, VALUE),
209     new KeyValue(TWO, CF, TWO, 1L, KeyValue.Type.Put, VALUE),
210   };
211 
212   private static class CellWithVersionsStoreScanner extends StoreScanner {
213     // Count of how often optimize is called and of how often it does an optimize.
214     final AtomicInteger optimization = new AtomicInteger(0);
215 
216     CellWithVersionsStoreScanner(final Scan scan, ScanInfo scanInfo, ScanType scanType)
217         throws IOException {
218       super(scan, scanInfo, scanType, scan.getFamilyMap().get(CF), Arrays
219           .<KeyValueScanner> asList(new KeyValueScanner[] { new KeyValueScanFixture(
220             KeyValue.COMPARATOR, CELL_WITH_VERSIONS) }));
221     }
222 
223     protected boolean trySkipToNextColumn(Cell cell) throws IOException {
224       boolean optimized = super.trySkipToNextColumn(cell);
225       LOG.info("Cell=" + cell + ", nextIndex=" + CellUtil.toString(getNextIndexedKey(), false)
226           + ", optimized=" + optimized);
227       if (optimized) {
228         optimization.incrementAndGet();
229       }
230       return optimized;
231     }
232 
233     @Override
234     public Cell getNextIndexedKey() {
235       // Fake block boundaries by having index of next block change as we go through scan.
236       return KeyValueUtil.createFirstOnRow(CELL_WITH_VERSIONS[CELL_WITH_VERSIONS_BLOCK2_BOUNDARY].getRow());
237     }
238   };
239 
240   private static class CellWithVersionsNoOptimizeStoreScanner extends StoreScanner {
241     // Count of how often optimize is called and of how often it does an optimize.
242     final AtomicInteger optimization = new AtomicInteger(0);
243 
244     CellWithVersionsNoOptimizeStoreScanner(final Scan scan, ScanInfo scanInfo, ScanType scanType)
245         throws IOException {
246       super(scan, scanInfo, scanType, scan.getFamilyMap().get(CF), Arrays
247           .<KeyValueScanner> asList(new KeyValueScanner[] { new KeyValueScanFixture(
248             KeyValue.COMPARATOR, CELL_WITH_VERSIONS) }));
249     }
250 
251     protected boolean trySkipToNextColumn(Cell cell) throws IOException {
252       boolean optimized = super.trySkipToNextColumn(cell);
253       LOG.info("Cell=" + cell + ", nextIndex=" + CellUtil.toString(getNextIndexedKey(), false)
254           + ", optimized=" + optimized);
255       if (optimized) {
256         optimization.incrementAndGet();
257       }
258       return optimized;
259     }
260 
261     @Override
262     public Cell getNextIndexedKey() {
263       return null;
264     }
265   };
266 
267   public void testWithColumnCountGetFilter() throws Exception {
268     Get get = new Get(ONE);
269     get.setMaxVersions();
270     get.addFamily(CF);
271     get.setFilter(new ColumnCountGetFilter(2));
272 
273     CellWithVersionsNoOptimizeStoreScanner scannerNoOptimize = new CellWithVersionsNoOptimizeStoreScanner(
274         new Scan(get), this.scanInfo, this.scanType);
275     try {
276       List<Cell> results = new ArrayList<>();
277       while (scannerNoOptimize.next(results)) {
278         continue;
279       }
280       assertEquals(2, results.size());
281       assertTrue(CellUtil.matchingColumn(results.get(0), CELL_WITH_VERSIONS[0]));
282       assertTrue(CellUtil.matchingColumn(results.get(1), CELL_WITH_VERSIONS[2]));
283       assertTrue("Optimize should do some optimizations",
284         scannerNoOptimize.optimization.get() == 0);
285     } finally {
286       scannerNoOptimize.close();
287     }
288 
289     get.setFilter(new ColumnCountGetFilter(2));
290     CellWithVersionsStoreScanner scanner = new CellWithVersionsStoreScanner(new Scan(get),
291         this.scanInfo, this.scanType);
292     try {
293       List<Cell> results = new ArrayList<>();
294       while (scanner.next(results)) {
295         continue;
296       }
297       assertEquals(2, results.size());
298       assertTrue(CellUtil.matchingColumn(results.get(0), CELL_WITH_VERSIONS[0]));
299       assertTrue(CellUtil.matchingColumn(results.get(1), CELL_WITH_VERSIONS[2]));
300       assertTrue("Optimize should do some optimizations", scanner.optimization.get() > 0);
301     } finally {
302       scanner.close();
303     }
304   }
305 
306   public void testFullRowGetDoesNotOverreadWhenRowInsideOneBlock() throws IOException {
307     // Do a Get against row two. Row two is inside a block that starts with row TWO but ends with
308     // row TWO_POINT_TWO. We should read one block only.
309     Get get = new Get(TWO);
310     Scan scan = new Scan(get);
311     CellGridStoreScanner scanner = new CellGridStoreScanner(scan, this.scanInfo, this.scanType);
312     try {
313       List<Cell> results = new ArrayList<>();
314       while (scanner.next(results)) {
315         continue;
316       }
317       // Should be four results of column 1 (though there are 5 rows in the CELL_GRID -- the
318       // TWO_POINT_TWO row does not have a a column ONE.
319       assertEquals(4, results.size());
320       // We should have gone the optimize route 5 times totally... an INCLUDE for the four cells
321       // in the row plus the DONE on the end.
322       assertEquals(5, scanner.count.get());
323       // For a full row Get, there should be no opportunity for scanner optimization.
324       assertEquals(0, scanner.optimization.get());
325     } finally {
326       scanner.close();
327     }
328   }
329 
330   public void testFullRowSpansBlocks() throws IOException {
331     // Do a Get against row FOUR. It spans two blocks.
332     Get get = new Get(FOUR);
333     Scan scan = new Scan(get);
334     CellGridStoreScanner scanner = new CellGridStoreScanner(scan, this.scanInfo, this.scanType);
335     try {
336       List<Cell> results = new ArrayList<>();
337       while (scanner.next(results)) {
338         continue;
339       }
340       // Should be four results of column 1 (though there are 5 rows in the CELL_GRID -- the
341       // TWO_POINT_TWO row does not have a a column ONE.
342       assertEquals(5, results.size());
343       // We should have gone the optimize route 6 times totally... an INCLUDE for the five cells
344       // in the row plus the DONE on the end.
345       assertEquals(6, scanner.count.get());
346       // For a full row Get, there should be no opportunity for scanner optimization.
347       assertEquals(0, scanner.optimization.get());
348     } finally {
349       scanner.close();
350     }
351   }
352 
353   /**
354    * Test optimize in StoreScanner. Test that we skip to the next 'block' when we it makes sense
355    * reading the block 'index'.
356    * @throws IOException
357    */
358   public void testOptimize() throws IOException {
359     Scan scan = new Scan();
360     // A scan that just gets the first qualifier on each row of the CELL_GRID
361     scan.addColumn(CF, ONE);
362     CellGridStoreScanner scanner = new CellGridStoreScanner(scan, this.scanInfo, this.scanType);
363     try {
364       List<Cell> results = new ArrayList<>();
365       while (scanner.next(results)) {
366         continue;
367       }
368       // Should be four results of column 1 (though there are 5 rows in the CELL_GRID -- the
369       // TWO_POINT_TWO row does not have a a column ONE.
370       assertEquals(4, results.size());
371       for (Cell cell: results) {
372         assertTrue(Bytes.equals(ONE, 0, ONE.length,
373             cell.getQualifierArray(), cell.getQualifierOffset(), cell.getQualifierLength()));
374       }
375       assertTrue("Optimize should do some optimizations", scanner.optimization.get() > 0);
376     } finally {
377       scanner.close();
378     }
379   }
380 
381   /**
382    * Ensure the optimize Scan method in StoreScanner does not get in the way of a Get doing minimum
383    * work... seeking to start of block and then SKIPPING until we find the wanted Cell.
384    * This 'simple' scenario mimics case of all Cells fitting inside a single HFileBlock.
385    * See HBASE-15392. This test is a little cryptic. Takes a bit of staring to figure what it up to.
386    * @throws IOException
387    */
388   public void testOptimizeAndGet() throws IOException {
389     // First test a Get of two columns in the row R2. Every Get is a Scan. Get columns named
390     // R2 and R3.
391     Get get = new Get(TWO);
392     get.addColumn(CF, TWO);
393     get.addColumn(CF, THREE);
394     Scan scan = new Scan(get);
395     CellGridStoreScanner scanner = new CellGridStoreScanner(scan, this.scanInfo, this.scanType);
396     try {
397       List<Cell> results = new ArrayList<>();
398       // For a Get there should be no more next's after the first call.
399       assertEquals(false, scanner.next(results));
400       // Should be one result only.
401       assertEquals(2, results.size());
402       // And we should have gone through optimize twice only.
403       assertEquals("First qcode is SEEK_NEXT_COL and second INCLUDE_AND_SEEK_NEXT_ROW",
404         3, scanner.count.get());
405     } finally {
406       scanner.close();
407     }
408   }
409 
410   /**
411    * Ensure that optimize does not cause the Get to do more seeking than required. Optimize
412    * (see HBASE-15392) was causing us to seek all Cells in a block when a Get Scan if the next block
413    * index/start key was a different row to the current one. A bug. We'd call next too often
414    * because we had to exhaust all Cells in the current row making us load the next block just to
415    * discard what we read there. This test is a little cryptic. Takes a bit of staring to figure
416    * what it up to.
417    * @throws IOException
418    */
419   public void testOptimizeAndGetWithFakedNextBlockIndexStart() throws IOException {
420     // First test a Get of second column in the row R2. Every Get is a Scan. Second column has a
421     // qualifier of R2.
422     Get get = new Get(THREE);
423     get.addColumn(CF, TWO);
424     Scan scan = new Scan(get);
425     CellGridStoreScanner scanner = new CellGridStoreScanner(scan, this.scanInfo, this.scanType);
426     try {
427       List<Cell> results = new ArrayList<>();
428       // For a Get there should be no more next's after the first call.
429       assertEquals(false, scanner.next(results));
430       // Should be one result only.
431       assertEquals(1, results.size());
432       // And we should have gone through optimize twice only.
433       assertEquals("First qcode is SEEK_NEXT_COL and second INCLUDE_AND_SEEK_NEXT_ROW",
434         2, scanner.count.get());
435     } finally {
436       scanner.close();
437     }
438   }
439 
440   /*
441    * Test utility for building a NavigableSet for scanners.
442    * @param strCols
443    * @return
444    */
445   NavigableSet<byte[]> getCols(String ...strCols) {
446     NavigableSet<byte[]> cols = new TreeSet<byte[]>(Bytes.BYTES_COMPARATOR);
447     for (String col : strCols) {
448       byte[] bytes = Bytes.toBytes(col);
449       cols.add(bytes);
450     }
451     return cols;
452   }
453 
454   public void testScanTimeRange() throws IOException {
455     String r1 = "R1";
456     // returns only 1 of these 2 even though same timestamp
457     KeyValue [] kvs = new KeyValue[] {
458         KeyValueTestUtil.create(r1, CF_STR, "a", 1, KeyValue.Type.Put, "dont-care"),
459         KeyValueTestUtil.create(r1, CF_STR, "a", 2, KeyValue.Type.Put, "dont-care"),
460         KeyValueTestUtil.create(r1, CF_STR, "a", 3, KeyValue.Type.Put, "dont-care"),
461         KeyValueTestUtil.create(r1, CF_STR, "a", 4, KeyValue.Type.Put, "dont-care"),
462         KeyValueTestUtil.create(r1, CF_STR, "a", 5, KeyValue.Type.Put, "dont-care"),
463     };
464     List<KeyValueScanner> scanners = Arrays.<KeyValueScanner>asList(
465         new KeyValueScanner[] {
466             new KeyValueScanFixture(KeyValue.COMPARATOR, kvs)
467     });
468     Scan scanSpec = new Scan(Bytes.toBytes(r1));
469     scanSpec.setTimeRange(0, 6);
470     scanSpec.setMaxVersions();
471     StoreScanner scan = new StoreScanner(scanSpec, scanInfo, scanType,
472         getCols("a"), scanners);
473     List<Cell> results = new ArrayList<Cell>();
474     assertEquals(true, scan.next(results));
475     assertEquals(5, results.size());
476     assertEquals(kvs[kvs.length - 1], results.get(0));
477     // Scan limited TimeRange
478     scanSpec = new Scan(Bytes.toBytes(r1));
479     scanSpec.setTimeRange(1, 3);
480     scanSpec.setMaxVersions();
481     scan = new StoreScanner(scanSpec, scanInfo, scanType, getCols("a"),
482         scanners);
483     results = new ArrayList<Cell>();
484     assertEquals(true, scan.next(results));
485     assertEquals(2, results.size());
486     // Another range.
487     scanSpec = new Scan(Bytes.toBytes(r1));
488     scanSpec.setTimeRange(5, 10);
489     scanSpec.setMaxVersions();
490     scan = new StoreScanner(scanSpec, scanInfo, scanType, getCols("a"),
491         scanners);
492     results = new ArrayList<Cell>();
493     assertEquals(true, scan.next(results));
494     assertEquals(1, results.size());
495     // See how TimeRange and Versions interact.
496     // Another range.
497     scanSpec = new Scan(Bytes.toBytes(r1));
498     scanSpec.setTimeRange(0, 10);
499     scanSpec.setMaxVersions(3);
500     scan = new StoreScanner(scanSpec, scanInfo, scanType, getCols("a"),
501         scanners);
502     results = new ArrayList<Cell>();
503     assertEquals(true, scan.next(results));
504     assertEquals(3, results.size());
505   }
506 
507   public void testScanSameTimestamp() throws IOException {
508     // returns only 1 of these 2 even though same timestamp
509     KeyValue [] kvs = new KeyValue[] {
510         KeyValueTestUtil.create("R1", "cf", "a", 1, KeyValue.Type.Put, "dont-care"),
511         KeyValueTestUtil.create("R1", "cf", "a", 1, KeyValue.Type.Put, "dont-care"),
512     };
513     List<KeyValueScanner> scanners = Arrays.asList(
514         new KeyValueScanner[] {
515             new KeyValueScanFixture(KeyValue.COMPARATOR, kvs)
516         });
517 
518     Scan scanSpec = new Scan(Bytes.toBytes("R1"));
519     // this only uses maxVersions (default=1) and TimeRange (default=all)
520     StoreScanner scan = new StoreScanner(scanSpec, scanInfo, scanType,
521         getCols("a"), scanners);
522 
523     List<Cell> results = new ArrayList<Cell>();
524     assertEquals(true, scan.next(results));
525     assertEquals(1, results.size());
526     assertEquals(kvs[0], results.get(0));
527   }
528 
529   /*
530    * Test test shows exactly how the matcher's return codes confuses the StoreScanner
531    * and prevent it from doing the right thing.  Seeking once, then nexting twice
532    * should return R1, then R2, but in this case it doesnt.
533    * TODO this comment makes no sense above. Appears to do the right thing.
534    * @throws IOException
535    */
536   public void testWontNextToNext() throws IOException {
537     // build the scan file:
538     KeyValue [] kvs = new KeyValue[] {
539         KeyValueTestUtil.create("R1", "cf", "a", 2, KeyValue.Type.Put, "dont-care"),
540         KeyValueTestUtil.create("R1", "cf", "a", 1, KeyValue.Type.Put, "dont-care"),
541         KeyValueTestUtil.create("R2", "cf", "a", 1, KeyValue.Type.Put, "dont-care")
542     };
543     List<KeyValueScanner> scanners = scanFixture(kvs);
544 
545     Scan scanSpec = new Scan(Bytes.toBytes("R1"));
546     // this only uses maxVersions (default=1) and TimeRange (default=all)
547     StoreScanner scan = new StoreScanner(scanSpec, scanInfo, scanType,
548         getCols("a"), scanners);
549 
550     List<Cell> results = new ArrayList<Cell>();
551     scan.next(results);
552     assertEquals(1, results.size());
553     assertEquals(kvs[0], results.get(0));
554     // should be ok...
555     // now scan _next_ again.
556     results.clear();
557     scan.next(results);
558     assertEquals(1, results.size());
559     assertEquals(kvs[2], results.get(0));
560 
561     results.clear();
562     scan.next(results);
563     assertEquals(0, results.size());
564 
565   }
566 
567 
568   public void testDeleteVersionSameTimestamp() throws IOException {
569     KeyValue [] kvs = new KeyValue [] {
570         KeyValueTestUtil.create("R1", "cf", "a", 1, KeyValue.Type.Put, "dont-care"),
571         KeyValueTestUtil.create("R1", "cf", "a", 1, KeyValue.Type.Delete, "dont-care"),
572     };
573     List<KeyValueScanner> scanners = scanFixture(kvs);
574     Scan scanSpec = new Scan(Bytes.toBytes("R1"));
575     StoreScanner scan = new StoreScanner(scanSpec, scanInfo, scanType,
576         getCols("a"), scanners);
577 
578     List<Cell> results = new ArrayList<Cell>();
579     assertFalse(scan.next(results));
580     assertEquals(0, results.size());
581   }
582 
583   /*
584    * Test the case where there is a delete row 'in front of' the next row, the scanner
585    * will move to the next row.
586    */
587   public void testDeletedRowThenGoodRow() throws IOException {
588     KeyValue [] kvs = new KeyValue [] {
589         KeyValueTestUtil.create("R1", "cf", "a", 1, KeyValue.Type.Put, "dont-care"),
590         KeyValueTestUtil.create("R1", "cf", "a", 1, KeyValue.Type.Delete, "dont-care"),
591         KeyValueTestUtil.create("R2", "cf", "a", 20, KeyValue.Type.Put, "dont-care")
592     };
593     List<KeyValueScanner> scanners = scanFixture(kvs);
594     Scan scanSpec = new Scan(Bytes.toBytes("R1"));
595     StoreScanner scan = new StoreScanner(scanSpec, scanInfo, scanType,
596         getCols("a"), scanners);
597 
598     List<Cell> results = new ArrayList<Cell>();
599     assertEquals(true, scan.next(results));
600     assertEquals(0, results.size());
601 
602     assertEquals(true, scan.next(results));
603     assertEquals(1, results.size());
604     assertEquals(kvs[2], results.get(0));
605 
606     assertEquals(false, scan.next(results));
607   }
608 
609   public void testDeleteVersionMaskingMultiplePuts() throws IOException {
610     long now = System.currentTimeMillis();
611     KeyValue [] kvs1 = new KeyValue[] {
612         KeyValueTestUtil.create("R1", "cf", "a", now, KeyValue.Type.Put, "dont-care"),
613         KeyValueTestUtil.create("R1", "cf", "a", now, KeyValue.Type.Delete, "dont-care")
614     };
615     KeyValue [] kvs2 = new KeyValue[] {
616         KeyValueTestUtil.create("R1", "cf", "a", now-500, KeyValue.Type.Put, "dont-care"),
617         KeyValueTestUtil.create("R1", "cf", "a", now-100, KeyValue.Type.Put, "dont-care"),
618         KeyValueTestUtil.create("R1", "cf", "a", now, KeyValue.Type.Put, "dont-care")
619     };
620     List<KeyValueScanner> scanners = scanFixture(kvs1, kvs2);
621 
622     StoreScanner scan = new StoreScanner(new Scan(Bytes.toBytes("R1")),
623         scanInfo, scanType, getCols("a"), scanners);
624     List<Cell> results = new ArrayList<Cell>();
625     // the two put at ts=now will be masked by the 1 delete, and
626     // since the scan default returns 1 version we'll return the newest
627     // key, which is kvs[2], now-100.
628     assertEquals(true, scan.next(results));
629     assertEquals(1, results.size());
630     assertEquals(kvs2[1], results.get(0));
631   }
632   public void testDeleteVersionsMixedAndMultipleVersionReturn() throws IOException {
633     long now = System.currentTimeMillis();
634     KeyValue [] kvs1 = new KeyValue[] {
635         KeyValueTestUtil.create("R1", "cf", "a", now, KeyValue.Type.Put, "dont-care"),
636         KeyValueTestUtil.create("R1", "cf", "a", now, KeyValue.Type.Delete, "dont-care")
637     };
638     KeyValue [] kvs2 = new KeyValue[] {
639         KeyValueTestUtil.create("R1", "cf", "a", now-500, KeyValue.Type.Put, "dont-care"),
640         KeyValueTestUtil.create("R1", "cf", "a", now+500, KeyValue.Type.Put, "dont-care"),
641         KeyValueTestUtil.create("R1", "cf", "a", now, KeyValue.Type.Put, "dont-care"),
642         KeyValueTestUtil.create("R2", "cf", "z", now, KeyValue.Type.Put, "dont-care")
643     };
644     List<KeyValueScanner> scanners = scanFixture(kvs1, kvs2);
645 
646     Scan scanSpec = new Scan(Bytes.toBytes("R1")).setMaxVersions(2);
647     StoreScanner scan = new StoreScanner(scanSpec, scanInfo, scanType,
648         getCols("a"), scanners);
649     List<Cell> results = new ArrayList<Cell>();
650     assertEquals(true, scan.next(results));
651     assertEquals(2, results.size());
652     assertEquals(kvs2[1], results.get(0));
653     assertEquals(kvs2[0], results.get(1));
654   }
655 
656   public void testWildCardOneVersionScan() throws IOException {
657     KeyValue [] kvs = new KeyValue [] {
658         KeyValueTestUtil.create("R1", "cf", "a", 2, KeyValue.Type.Put, "dont-care"),
659         KeyValueTestUtil.create("R1", "cf", "b", 1, KeyValue.Type.Put, "dont-care"),
660         KeyValueTestUtil.create("R1", "cf", "a", 1, KeyValue.Type.DeleteColumn, "dont-care"),
661     };
662     List<KeyValueScanner> scanners = scanFixture(kvs);
663     StoreScanner scan = new StoreScanner(new Scan(Bytes.toBytes("R1")),
664         scanInfo, scanType, null, scanners);
665     List<Cell> results = new ArrayList<Cell>();
666     assertEquals(true, scan.next(results));
667     assertEquals(2, results.size());
668     assertEquals(kvs[0], results.get(0));
669     assertEquals(kvs[1], results.get(1));
670   }
671 
672   public void testWildCardScannerUnderDeletes() throws IOException {
673     KeyValue [] kvs = new KeyValue [] {
674         KeyValueTestUtil.create("R1", "cf", "a", 2, KeyValue.Type.Put, "dont-care"), // inc
675         // orphaned delete column.
676         KeyValueTestUtil.create("R1", "cf", "a", 1, KeyValue.Type.DeleteColumn, "dont-care"),
677         // column b
678         KeyValueTestUtil.create("R1", "cf", "b", 2, KeyValue.Type.Put, "dont-care"), // inc
679         KeyValueTestUtil.create("R1", "cf", "b", 1, KeyValue.Type.Put, "dont-care"), // inc
680         // column c
681         KeyValueTestUtil.create("R1", "cf", "c", 10, KeyValue.Type.Delete, "dont-care"),
682         KeyValueTestUtil.create("R1", "cf", "c", 10, KeyValue.Type.Put, "dont-care"), // no
683         KeyValueTestUtil.create("R1", "cf", "c", 9, KeyValue.Type.Put, "dont-care"),  // inc
684         // column d
685         KeyValueTestUtil.create("R1", "cf", "d", 11, KeyValue.Type.Put, "dont-care"), // inc
686         KeyValueTestUtil.create("R1", "cf", "d", 10, KeyValue.Type.DeleteColumn, "dont-care"),
687         KeyValueTestUtil.create("R1", "cf", "d", 9, KeyValue.Type.Put, "dont-care"),  // no
688         KeyValueTestUtil.create("R1", "cf", "d", 8, KeyValue.Type.Put, "dont-care"),  // no
689 
690     };
691     List<KeyValueScanner> scanners = scanFixture(kvs);
692     StoreScanner scan = new StoreScanner(new Scan().setMaxVersions(2),
693         scanInfo, scanType, null, scanners);
694     List<Cell> results = new ArrayList<Cell>();
695     assertEquals(true, scan.next(results));
696     assertEquals(5, results.size());
697     assertEquals(kvs[0], results.get(0));
698     assertEquals(kvs[2], results.get(1));
699     assertEquals(kvs[3], results.get(2));
700     assertEquals(kvs[6], results.get(3));
701     assertEquals(kvs[7], results.get(4));
702   }
703 
704   public void testDeleteFamily() throws IOException {
705     KeyValue [] kvs = new KeyValue[] {
706         KeyValueTestUtil.create("R1", "cf", "a", 100, KeyValue.Type.DeleteFamily, "dont-care"),
707         KeyValueTestUtil.create("R1", "cf", "b", 11, KeyValue.Type.Put, "dont-care"),
708         KeyValueTestUtil.create("R1", "cf", "c", 11, KeyValue.Type.Put, "dont-care"),
709         KeyValueTestUtil.create("R1", "cf", "d", 11, KeyValue.Type.Put, "dont-care"),
710         KeyValueTestUtil.create("R1", "cf", "e", 11, KeyValue.Type.Put, "dont-care"),
711         KeyValueTestUtil.create("R1", "cf", "e", 11, KeyValue.Type.DeleteColumn, "dont-care"),
712         KeyValueTestUtil.create("R1", "cf", "f", 11, KeyValue.Type.Put, "dont-care"),
713         KeyValueTestUtil.create("R1", "cf", "g", 11, KeyValue.Type.Put, "dont-care"),
714         KeyValueTestUtil.create("R1", "cf", "g", 11, KeyValue.Type.Delete, "dont-care"),
715         KeyValueTestUtil.create("R1", "cf", "h", 11, KeyValue.Type.Put, "dont-care"),
716         KeyValueTestUtil.create("R1", "cf", "i", 11, KeyValue.Type.Put, "dont-care"),
717         KeyValueTestUtil.create("R2", "cf", "a", 11, KeyValue.Type.Put, "dont-care"),
718     };
719     List<KeyValueScanner> scanners = scanFixture(kvs);
720     StoreScanner scan = new StoreScanner(
721         new Scan().setMaxVersions(Integer.MAX_VALUE), scanInfo, scanType, null,
722         scanners);
723     List<Cell> results = new ArrayList<Cell>();
724     assertEquals(true, scan.next(results));
725     assertEquals(0, results.size());
726     assertEquals(true, scan.next(results));
727     assertEquals(1, results.size());
728     assertEquals(kvs[kvs.length-1], results.get(0));
729 
730     assertEquals(false, scan.next(results));
731   }
732 
733   public void testDeleteColumn() throws IOException {
734     KeyValue [] kvs = new KeyValue[] {
735         KeyValueTestUtil.create("R1", "cf", "a", 10, KeyValue.Type.DeleteColumn, "dont-care"),
736         KeyValueTestUtil.create("R1", "cf", "a", 9, KeyValue.Type.Delete, "dont-care"),
737         KeyValueTestUtil.create("R1", "cf", "a", 8, KeyValue.Type.Put, "dont-care"),
738         KeyValueTestUtil.create("R1", "cf", "b", 5, KeyValue.Type.Put, "dont-care")
739     };
740     List<KeyValueScanner> scanners = scanFixture(kvs);
741     StoreScanner scan = new StoreScanner(new Scan(), scanInfo, scanType, null,
742         scanners);
743     List<Cell> results = new ArrayList<Cell>();
744     assertEquals(true, scan.next(results));
745     assertEquals(1, results.size());
746     assertEquals(kvs[3], results.get(0));
747   }
748 
749   private static final  KeyValue [] kvs = new KeyValue[] {
750         KeyValueTestUtil.create("R1", "cf", "a", 11, KeyValue.Type.Put, "dont-care"),
751         KeyValueTestUtil.create("R1", "cf", "b", 11, KeyValue.Type.Put, "dont-care"),
752         KeyValueTestUtil.create("R1", "cf", "c", 11, KeyValue.Type.Put, "dont-care"),
753         KeyValueTestUtil.create("R1", "cf", "d", 11, KeyValue.Type.Put, "dont-care"),
754         KeyValueTestUtil.create("R1", "cf", "e", 11, KeyValue.Type.Put, "dont-care"),
755         KeyValueTestUtil.create("R1", "cf", "f", 11, KeyValue.Type.Put, "dont-care"),
756         KeyValueTestUtil.create("R1", "cf", "g", 11, KeyValue.Type.Put, "dont-care"),
757         KeyValueTestUtil.create("R1", "cf", "h", 11, KeyValue.Type.Put, "dont-care"),
758         KeyValueTestUtil.create("R1", "cf", "i", 11, KeyValue.Type.Put, "dont-care"),
759         KeyValueTestUtil.create("R2", "cf", "a", 11, KeyValue.Type.Put, "dont-care"),
760     };
761 
762   public void testSkipColumn() throws IOException {
763     List<KeyValueScanner> scanners = scanFixture(kvs);
764     StoreScanner scan = new StoreScanner(new Scan(), scanInfo, scanType,
765         getCols("a", "d"), scanners);
766 
767     List<Cell> results = new ArrayList<Cell>();
768     assertEquals(true, scan.next(results));
769     assertEquals(2, results.size());
770     assertEquals(kvs[0], results.get(0));
771     assertEquals(kvs[3], results.get(1));
772     results.clear();
773 
774     assertEquals(true, scan.next(results));
775     assertEquals(1, results.size());
776     assertEquals(kvs[kvs.length-1], results.get(0));
777 
778     results.clear();
779     assertEquals(false, scan.next(results));
780   }
781 
782   /*
783    * Test expiration of KeyValues in combination with a configured TTL for
784    * a column family (as should be triggered in a major compaction).
785    */
786   public void testWildCardTtlScan() throws IOException {
787     long now = System.currentTimeMillis();
788     KeyValue [] kvs = new KeyValue[] {
789         KeyValueTestUtil.create("R1", "cf", "a", now-1000, KeyValue.Type.Put, "dont-care"),
790         KeyValueTestUtil.create("R1", "cf", "b", now-10, KeyValue.Type.Put, "dont-care"),
791         KeyValueTestUtil.create("R1", "cf", "c", now-200, KeyValue.Type.Put, "dont-care"),
792         KeyValueTestUtil.create("R1", "cf", "d", now-10000, KeyValue.Type.Put, "dont-care"),
793         KeyValueTestUtil.create("R2", "cf", "a", now, KeyValue.Type.Put, "dont-care"),
794         KeyValueTestUtil.create("R2", "cf", "b", now-10, KeyValue.Type.Put, "dont-care"),
795         KeyValueTestUtil.create("R2", "cf", "c", now-200, KeyValue.Type.Put, "dont-care"),
796         KeyValueTestUtil.create("R2", "cf", "c", now-1000, KeyValue.Type.Put, "dont-care")
797     };
798     List<KeyValueScanner> scanners = scanFixture(kvs);
799     Scan scan = new Scan();
800     scan.setMaxVersions(1);
801     ScanInfo scanInfo = new ScanInfo(CONF, CF, 0, 1, 500, KeepDeletedCells.FALSE, 0,
802         KeyValue.COMPARATOR);
803     ScanType scanType = ScanType.USER_SCAN;
804     StoreScanner scanner =
805       new StoreScanner(scan, scanInfo, scanType,
806           null, scanners);
807 
808     List<Cell> results = new ArrayList<Cell>();
809     assertEquals(true, scanner.next(results));
810     assertEquals(2, results.size());
811     assertEquals(kvs[1], results.get(0));
812     assertEquals(kvs[2], results.get(1));
813     results.clear();
814 
815     assertEquals(true, scanner.next(results));
816     assertEquals(3, results.size());
817     assertEquals(kvs[4], results.get(0));
818     assertEquals(kvs[5], results.get(1));
819     assertEquals(kvs[6], results.get(2));
820     results.clear();
821 
822     assertEquals(false, scanner.next(results));
823   }
824 
825   public void testScannerReseekDoesntNPE() throws Exception {
826     List<KeyValueScanner> scanners = scanFixture(kvs);
827     StoreScanner scan = new StoreScanner(new Scan(), scanInfo, scanType,
828         getCols("a", "d"), scanners);
829 
830     // Previously a updateReaders twice in a row would cause an NPE.  In test this would also
831     // normally cause an NPE because scan.store is null.  So as long as we get through these
832     // two calls we are good and the bug was quashed.
833 
834     scan.updateReaders(Collections.EMPTY_LIST, Collections.EMPTY_LIST);
835 
836     scan.updateReaders(Collections.EMPTY_LIST, Collections.EMPTY_LIST);
837 
838     scan.peek();
839   }
840 
841 
842   /**
843    * TODO this fails, since we don't handle deletions, etc, in peek
844    */
845   public void SKIP_testPeek() throws Exception {
846     KeyValue [] kvs = new KeyValue [] {
847         KeyValueTestUtil.create("R1", "cf", "a", 1, KeyValue.Type.Put, "dont-care"),
848         KeyValueTestUtil.create("R1", "cf", "a", 1, KeyValue.Type.Delete, "dont-care"),
849     };
850     List<KeyValueScanner> scanners = scanFixture(kvs);
851     Scan scanSpec = new Scan(Bytes.toBytes("R1"));
852     StoreScanner scan = new StoreScanner(scanSpec, scanInfo, scanType,
853         getCols("a"), scanners);
854     assertNull(scan.peek());
855   }
856 
857   /**
858    * Ensure that expired delete family markers don't override valid puts
859    */
860   public void testExpiredDeleteFamily() throws Exception {
861     long now = System.currentTimeMillis();
862     KeyValue [] kvs = new KeyValue[] {
863         new KeyValue(Bytes.toBytes("R1"), Bytes.toBytes("cf"), null, now-1000,
864             KeyValue.Type.DeleteFamily),
865         KeyValueTestUtil.create("R1", "cf", "a", now-10, KeyValue.Type.Put,
866             "dont-care"),
867     };
868     List<KeyValueScanner> scanners = scanFixture(kvs);
869     Scan scan = new Scan();
870     scan.setMaxVersions(1);
871     // scanner with ttl equal to 500
872     ScanInfo scanInfo = new ScanInfo(CONF, CF, 0, 1, 500, KeepDeletedCells.FALSE, 0,
873         KeyValue.COMPARATOR);
874     ScanType scanType = ScanType.USER_SCAN;
875     StoreScanner scanner =
876         new StoreScanner(scan, scanInfo, scanType, null, scanners);
877 
878     List<Cell> results = new ArrayList<Cell>();
879     assertEquals(true, scanner.next(results));
880     assertEquals(1, results.size());
881     assertEquals(kvs[1], results.get(0));
882     results.clear();
883 
884     assertEquals(false, scanner.next(results));
885   }
886 
887   public void testDeleteMarkerLongevity() throws Exception {
888     try {
889       final long now = System.currentTimeMillis();
890       EnvironmentEdgeManagerTestHelper.injectEdge(new EnvironmentEdge() {
891         public long currentTime() {
892           return now;
893         }
894       });
895       KeyValue[] kvs = new KeyValue[]{
896         /*0*/ new KeyValue(Bytes.toBytes("R1"), Bytes.toBytes("cf"), null,
897         now - 100, KeyValue.Type.DeleteFamily), // live
898         /*1*/ new KeyValue(Bytes.toBytes("R1"), Bytes.toBytes("cf"), null,
899         now - 1000, KeyValue.Type.DeleteFamily), // expired
900         /*2*/ KeyValueTestUtil.create("R1", "cf", "a", now - 50,
901         KeyValue.Type.Put, "v3"), // live
902         /*3*/ KeyValueTestUtil.create("R1", "cf", "a", now - 55,
903         KeyValue.Type.Delete, "dontcare"), // live
904         /*4*/ KeyValueTestUtil.create("R1", "cf", "a", now - 55,
905         KeyValue.Type.Put, "deleted-version v2"), // deleted
906         /*5*/ KeyValueTestUtil.create("R1", "cf", "a", now - 60,
907         KeyValue.Type.Put, "v1"), // live
908         /*6*/ KeyValueTestUtil.create("R1", "cf", "a", now - 65,
909         KeyValue.Type.Put, "v0"), // max-version reached
910         /*7*/ KeyValueTestUtil.create("R1", "cf", "a",
911         now - 100, KeyValue.Type.DeleteColumn, "dont-care"), // max-version
912         /*8*/ KeyValueTestUtil.create("R1", "cf", "b", now - 600,
913         KeyValue.Type.DeleteColumn, "dont-care"), //expired
914         /*9*/ KeyValueTestUtil.create("R1", "cf", "b", now - 70,
915         KeyValue.Type.Put, "v2"), //live
916         /*10*/ KeyValueTestUtil.create("R1", "cf", "b", now - 750,
917         KeyValue.Type.Put, "v1"), //expired
918         /*11*/ KeyValueTestUtil.create("R1", "cf", "c", now - 500,
919         KeyValue.Type.Delete, "dontcare"), //expired
920         /*12*/ KeyValueTestUtil.create("R1", "cf", "c", now - 600,
921         KeyValue.Type.Put, "v1"), //expired
922         /*13*/ KeyValueTestUtil.create("R1", "cf", "c", now - 1000,
923         KeyValue.Type.Delete, "dontcare"), //expired
924         /*14*/ KeyValueTestUtil.create("R1", "cf", "d", now - 60,
925         KeyValue.Type.Put, "expired put"), //live
926         /*15*/ KeyValueTestUtil.create("R1", "cf", "d", now - 100,
927         KeyValue.Type.Delete, "not-expired delete"), //live
928       };
929       List<KeyValueScanner> scanners = scanFixture(kvs);
930       Scan scan = new Scan();
931       scan.setMaxVersions(2);
932       ScanInfo scanInfo = new ScanInfo(CONF, Bytes.toBytes("cf"),
933         0 /* minVersions */,
934         2 /* maxVersions */, 500 /* ttl */,
935         KeepDeletedCells.FALSE /* keepDeletedCells */,
936         200, /* timeToPurgeDeletes */
937         KeyValue.COMPARATOR);
938       StoreScanner scanner =
939         new StoreScanner(scan, scanInfo,
940           ScanType.COMPACT_DROP_DELETES, null, scanners,
941           HConstants.OLDEST_TIMESTAMP);
942       List<Cell> results = new ArrayList<Cell>();
943       results = new ArrayList<Cell>();
944       assertEquals(true, scanner.next(results));
945       assertEquals(kvs[0], results.get(0));
946       assertEquals(kvs[2], results.get(1));
947       assertEquals(kvs[3], results.get(2));
948       assertEquals(kvs[5], results.get(3));
949       assertEquals(kvs[9], results.get(4));
950       assertEquals(kvs[14], results.get(5));
951       assertEquals(kvs[15], results.get(6));
952       assertEquals(7, results.size());
953       scanner.close();
954     }finally{
955     EnvironmentEdgeManagerTestHelper.reset();
956     }
957   }
958 
959 }
960