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