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