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