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