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