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