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