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