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