001/** 002 * 003 * Licensed to the Apache Software Foundation (ASF) under one 004 * or more contributor license agreements. See the NOTICE file 005 * distributed with this work for additional information 006 * regarding copyright ownership. The ASF licenses this file 007 * to you under the Apache License, Version 2.0 (the 008 * "License"); you may not use this file except in compliance 009 * with the License. You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, software 014 * distributed under the License is distributed on an "AS IS" BASIS, 015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 016 * See the License for the specific language governing permissions and 017 * limitations under the License. 018 */ 019package org.apache.hadoop.hbase.regionserver; 020 021import java.io.IOException; 022import java.io.InterruptedIOException; 023import java.util.ArrayList; 024import java.util.List; 025import java.util.NavigableSet; 026import java.util.OptionalInt; 027import java.util.concurrent.CountDownLatch; 028import java.util.concurrent.locks.ReentrantLock; 029 030import org.apache.hadoop.hbase.Cell; 031import org.apache.hadoop.hbase.CellComparator; 032import org.apache.hadoop.hbase.CellUtil; 033import org.apache.hadoop.hbase.DoNotRetryIOException; 034import org.apache.hadoop.hbase.HConstants; 035import org.apache.hadoop.hbase.PrivateCellUtil; 036import org.apache.hadoop.hbase.KeyValue; 037import org.apache.hadoop.hbase.KeyValueUtil; 038import org.apache.hadoop.hbase.client.IsolationLevel; 039import org.apache.hadoop.hbase.client.Scan; 040import org.apache.hadoop.hbase.executor.ExecutorService; 041import org.apache.hadoop.hbase.filter.Filter; 042import org.apache.hadoop.hbase.regionserver.ScannerContext.LimitScope; 043import org.apache.hadoop.hbase.regionserver.ScannerContext.NextState; 044import org.apache.hadoop.hbase.regionserver.handler.ParallelSeekHandler; 045import org.apache.hadoop.hbase.regionserver.querymatcher.CompactionScanQueryMatcher; 046import org.apache.hadoop.hbase.regionserver.querymatcher.ScanQueryMatcher; 047import org.apache.hadoop.hbase.regionserver.querymatcher.UserScanQueryMatcher; 048import org.apache.hadoop.hbase.util.CollectionUtils; 049import org.apache.hadoop.hbase.util.EnvironmentEdgeManager; 050import org.apache.yetus.audience.InterfaceAudience; 051import org.slf4j.Logger; 052import org.slf4j.LoggerFactory; 053import org.apache.hbase.thirdparty.com.google.common.annotations.VisibleForTesting; 054import org.apache.hbase.thirdparty.com.google.common.base.Preconditions; 055 056/** 057 * Scanner scans both the memstore and the Store. Coalesce KeyValue stream into List<KeyValue> 058 * for a single row. 059 * <p> 060 * The implementation is not thread safe. So there will be no race between next and close. The only 061 * exception is updateReaders, it will be called in the memstore flush thread to indicate that there 062 * is a flush. 063 */ 064@InterfaceAudience.Private 065public class StoreScanner extends NonReversedNonLazyKeyValueScanner 066 implements KeyValueScanner, InternalScanner, ChangedReadersObserver { 067 private static final Logger LOG = LoggerFactory.getLogger(StoreScanner.class); 068 // In unit tests, the store could be null 069 protected final HStore store; 070 private final CellComparator comparator; 071 private ScanQueryMatcher matcher; 072 protected KeyValueHeap heap; 073 private boolean cacheBlocks; 074 075 private long countPerRow = 0; 076 private int storeLimit = -1; 077 private int storeOffset = 0; 078 079 // Used to indicate that the scanner has closed (see HBASE-1107) 080 // Do not need to be volatile because it's always accessed via synchronized methods 081 private boolean closing = false; 082 private final boolean get; 083 private final boolean explicitColumnQuery; 084 private final boolean useRowColBloom; 085 /** 086 * A flag that enables StoreFileScanner parallel-seeking 087 */ 088 private boolean parallelSeekEnabled = false; 089 private ExecutorService executor; 090 private final Scan scan; 091 private final long oldestUnexpiredTS; 092 private final long now; 093 private final int minVersions; 094 private final long maxRowSize; 095 private final long cellsPerHeartbeatCheck; 096 097 // 1) Collects all the KVHeap that are eagerly getting closed during the 098 // course of a scan 099 // 2) Collects the unused memstore scanners. If we close the memstore scanners 100 // before sending data to client, the chunk may be reclaimed by other 101 // updates and the data will be corrupt. 102 private final List<KeyValueScanner> scannersForDelayedClose = new ArrayList<>(); 103 104 /** 105 * The number of KVs seen by the scanner. Includes explicitly skipped KVs, but not 106 * KVs skipped via seeking to next row/column. TODO: estimate them? 107 */ 108 private long kvsScanned = 0; 109 private Cell prevCell = null; 110 111 private final long preadMaxBytes; 112 private long bytesRead; 113 114 /** We don't ever expect to change this, the constant is just for clarity. */ 115 static final boolean LAZY_SEEK_ENABLED_BY_DEFAULT = true; 116 public static final String STORESCANNER_PARALLEL_SEEK_ENABLE = 117 "hbase.storescanner.parallel.seek.enable"; 118 119 /** Used during unit testing to ensure that lazy seek does save seek ops */ 120 private static boolean lazySeekEnabledGlobally = LAZY_SEEK_ENABLED_BY_DEFAULT; 121 122 /** 123 * The number of cells scanned in between timeout checks. Specifying a larger value means that 124 * timeout checks will occur less frequently. Specifying a small value will lead to more frequent 125 * timeout checks. 126 */ 127 public static final String HBASE_CELLS_SCANNED_PER_HEARTBEAT_CHECK = 128 "hbase.cells.scanned.per.heartbeat.check"; 129 130 /** 131 * Default value of {@link #HBASE_CELLS_SCANNED_PER_HEARTBEAT_CHECK}. 132 */ 133 public static final long DEFAULT_HBASE_CELLS_SCANNED_PER_HEARTBEAT_CHECK = 10000; 134 135 /** 136 * If the read type if Scan.ReadType.DEFAULT, we will start with pread, and if the kvs we scanned 137 * reaches this limit, we will reopen the scanner with stream. The default value is 4 times of 138 * block size for this store. 139 */ 140 public static final String STORESCANNER_PREAD_MAX_BYTES = "hbase.storescanner.pread.max.bytes"; 141 142 private final Scan.ReadType readType; 143 144 // A flag whether use pread for scan 145 // it maybe changed if we use Scan.ReadType.DEFAULT and we have read lots of data. 146 private boolean scanUsePread; 147 // Indicates whether there was flush during the course of the scan 148 private volatile boolean flushed = false; 149 // generally we get one file from a flush 150 private final List<KeyValueScanner> flushedstoreFileScanners = new ArrayList<>(1); 151 // Since CompactingMemstore is now default, we get three memstore scanners from a flush 152 private final List<KeyValueScanner> memStoreScannersAfterFlush = new ArrayList<>(3); 153 // The current list of scanners 154 @VisibleForTesting 155 final List<KeyValueScanner> currentScanners = new ArrayList<>(); 156 // flush update lock 157 private final ReentrantLock flushLock = new ReentrantLock(); 158 159 protected final long readPt; 160 private boolean topChanged = false; 161 162 /** An internal constructor. */ 163 private StoreScanner(HStore store, Scan scan, ScanInfo scanInfo, 164 int numColumns, long readPt, boolean cacheBlocks, ScanType scanType) { 165 this.readPt = readPt; 166 this.store = store; 167 this.cacheBlocks = cacheBlocks; 168 this.comparator = Preconditions.checkNotNull(scanInfo.getComparator()); 169 get = scan.isGetScan(); 170 explicitColumnQuery = numColumns > 0; 171 this.scan = scan; 172 this.now = EnvironmentEdgeManager.currentTime(); 173 this.oldestUnexpiredTS = scan.isRaw() ? 0L : now - scanInfo.getTtl(); 174 this.minVersions = scanInfo.getMinVersions(); 175 176 // We look up row-column Bloom filters for multi-column queries as part of 177 // the seek operation. However, we also look the row-column Bloom filter 178 // for multi-row (non-"get") scans because this is not done in 179 // StoreFile.passesBloomFilter(Scan, SortedSet<byte[]>). 180 this.useRowColBloom = numColumns > 1 || (!get && numColumns == 1); 181 this.maxRowSize = scanInfo.getTableMaxRowSize(); 182 if (get) { 183 this.readType = Scan.ReadType.PREAD; 184 this.scanUsePread = true; 185 } else if(scanType != ScanType.USER_SCAN) { 186 // For compaction scanners never use Pread as already we have stream based scanners on the 187 // store files to be compacted 188 this.readType = Scan.ReadType.STREAM; 189 this.scanUsePread = false; 190 } else { 191 if (scan.getReadType() == Scan.ReadType.DEFAULT) { 192 this.readType = scanInfo.isUsePread() ? Scan.ReadType.PREAD : Scan.ReadType.DEFAULT; 193 } else { 194 this.readType = scan.getReadType(); 195 } 196 // Always start with pread unless user specific stream. Will change to stream later if 197 // readType is default if the scan keeps running for a long time. 198 this.scanUsePread = this.readType != Scan.ReadType.STREAM; 199 } 200 this.preadMaxBytes = scanInfo.getPreadMaxBytes(); 201 this.cellsPerHeartbeatCheck = scanInfo.getCellsPerTimeoutCheck(); 202 // Parallel seeking is on if the config allows and more there is more than one store file. 203 if (store != null && store.getStorefilesCount() > 1) { 204 RegionServerServices rsService = store.getHRegion().getRegionServerServices(); 205 if (rsService != null && scanInfo.isParallelSeekEnabled()) { 206 this.parallelSeekEnabled = true; 207 this.executor = rsService.getExecutorService(); 208 } 209 } 210 } 211 212 private void addCurrentScanners(List<? extends KeyValueScanner> scanners) { 213 this.currentScanners.addAll(scanners); 214 } 215 216 /** 217 * Opens a scanner across memstore, snapshot, and all StoreFiles. Assumes we 218 * are not in a compaction. 219 * 220 * @param store who we scan 221 * @param scan the spec 222 * @param columns which columns we are scanning 223 * @throws IOException 224 */ 225 public StoreScanner(HStore store, ScanInfo scanInfo, Scan scan, NavigableSet<byte[]> columns, 226 long readPt) throws IOException { 227 this(store, scan, scanInfo, columns != null ? columns.size() : 0, readPt, 228 scan.getCacheBlocks(), ScanType.USER_SCAN); 229 if (columns != null && scan.isRaw()) { 230 throw new DoNotRetryIOException("Cannot specify any column for a raw scan"); 231 } 232 matcher = UserScanQueryMatcher.create(scan, scanInfo, columns, oldestUnexpiredTS, now, 233 store.getCoprocessorHost()); 234 235 store.addChangedReaderObserver(this); 236 237 try { 238 // Pass columns to try to filter out unnecessary StoreFiles. 239 List<KeyValueScanner> scanners = selectScannersFrom(store, 240 store.getScanners(cacheBlocks, scanUsePread, false, matcher, scan.getStartRow(), 241 scan.includeStartRow(), scan.getStopRow(), scan.includeStopRow(), this.readPt)); 242 243 // Seek all scanners to the start of the Row (or if the exact matching row 244 // key does not exist, then to the start of the next matching Row). 245 // Always check bloom filter to optimize the top row seek for delete 246 // family marker. 247 seekScanners(scanners, matcher.getStartKey(), explicitColumnQuery && lazySeekEnabledGlobally, 248 parallelSeekEnabled); 249 250 // set storeLimit 251 this.storeLimit = scan.getMaxResultsPerColumnFamily(); 252 253 // set rowOffset 254 this.storeOffset = scan.getRowOffsetPerColumnFamily(); 255 addCurrentScanners(scanners); 256 // Combine all seeked scanners with a heap 257 resetKVHeap(scanners, comparator); 258 } catch (IOException e) { 259 // remove us from the HStore#changedReaderObservers here or we'll have no chance to 260 // and might cause memory leak 261 store.deleteChangedReaderObserver(this); 262 throw e; 263 } 264 } 265 266 // a dummy scan instance for compaction. 267 private static final Scan SCAN_FOR_COMPACTION = new Scan(); 268 269 /** 270 * Used for store file compaction and memstore compaction. 271 * <p> 272 * Opens a scanner across specified StoreFiles/MemStoreSegments. 273 * @param store who we scan 274 * @param scanners ancillary scanners 275 * @param smallestReadPoint the readPoint that we should use for tracking versions 276 */ 277 public StoreScanner(HStore store, ScanInfo scanInfo, List<? extends KeyValueScanner> scanners, 278 ScanType scanType, long smallestReadPoint, long earliestPutTs) throws IOException { 279 this(store, scanInfo, scanners, scanType, smallestReadPoint, earliestPutTs, null, null); 280 } 281 282 /** 283 * Used for compactions that drop deletes from a limited range of rows. 284 * <p> 285 * Opens a scanner across specified StoreFiles. 286 * @param store who we scan 287 * @param scanners ancillary scanners 288 * @param smallestReadPoint the readPoint that we should use for tracking versions 289 * @param dropDeletesFromRow The inclusive left bound of the range; can be EMPTY_START_ROW. 290 * @param dropDeletesToRow The exclusive right bound of the range; can be EMPTY_END_ROW. 291 */ 292 public StoreScanner(HStore store, ScanInfo scanInfo, List<? extends KeyValueScanner> scanners, 293 long smallestReadPoint, long earliestPutTs, byte[] dropDeletesFromRow, 294 byte[] dropDeletesToRow) throws IOException { 295 this(store, scanInfo, scanners, ScanType.COMPACT_RETAIN_DELETES, smallestReadPoint, 296 earliestPutTs, dropDeletesFromRow, dropDeletesToRow); 297 } 298 299 private StoreScanner(HStore store, ScanInfo scanInfo, List<? extends KeyValueScanner> scanners, 300 ScanType scanType, long smallestReadPoint, long earliestPutTs, byte[] dropDeletesFromRow, 301 byte[] dropDeletesToRow) throws IOException { 302 this(store, SCAN_FOR_COMPACTION, scanInfo, 0, 303 store.getHRegion().getReadPoint(IsolationLevel.READ_COMMITTED), false, scanType); 304 assert scanType != ScanType.USER_SCAN; 305 matcher = 306 CompactionScanQueryMatcher.create(scanInfo, scanType, smallestReadPoint, earliestPutTs, 307 oldestUnexpiredTS, now, dropDeletesFromRow, dropDeletesToRow, store.getCoprocessorHost()); 308 309 // Filter the list of scanners using Bloom filters, time range, TTL, etc. 310 scanners = selectScannersFrom(store, scanners); 311 312 // Seek all scanners to the initial key 313 seekScanners(scanners, matcher.getStartKey(), false, parallelSeekEnabled); 314 addCurrentScanners(scanners); 315 // Combine all seeked scanners with a heap 316 resetKVHeap(scanners, comparator); 317 } 318 319 private void seekAllScanner(ScanInfo scanInfo, List<? extends KeyValueScanner> scanners) 320 throws IOException { 321 // Seek all scanners to the initial key 322 seekScanners(scanners, matcher.getStartKey(), false, parallelSeekEnabled); 323 addCurrentScanners(scanners); 324 resetKVHeap(scanners, comparator); 325 } 326 327 // For mob compaction only as we do not have a Store instance when doing mob compaction. 328 public StoreScanner(ScanInfo scanInfo, ScanType scanType, 329 List<? extends KeyValueScanner> scanners) throws IOException { 330 this(null, SCAN_FOR_COMPACTION, scanInfo, 0, Long.MAX_VALUE, false, scanType); 331 assert scanType != ScanType.USER_SCAN; 332 this.matcher = CompactionScanQueryMatcher.create(scanInfo, scanType, Long.MAX_VALUE, 0L, 333 oldestUnexpiredTS, now, null, null, null); 334 seekAllScanner(scanInfo, scanners); 335 } 336 337 // Used to instantiate a scanner for user scan in test 338 @VisibleForTesting 339 StoreScanner(Scan scan, ScanInfo scanInfo, NavigableSet<byte[]> columns, 340 List<? extends KeyValueScanner> scanners) throws IOException { 341 // 0 is passed as readpoint because the test bypasses Store 342 this(null, scan, scanInfo, columns != null ? columns.size() : 0, 0L, 343 scan.getCacheBlocks(), ScanType.USER_SCAN); 344 this.matcher = 345 UserScanQueryMatcher.create(scan, scanInfo, columns, oldestUnexpiredTS, now, null); 346 seekAllScanner(scanInfo, scanners); 347 } 348 349 // Used to instantiate a scanner for compaction in test 350 @VisibleForTesting 351 StoreScanner(ScanInfo scanInfo, OptionalInt maxVersions, ScanType scanType, 352 List<? extends KeyValueScanner> scanners) throws IOException { 353 // 0 is passed as readpoint because the test bypasses Store 354 this(null, maxVersions.isPresent() ? new Scan().readVersions(maxVersions.getAsInt()) 355 : SCAN_FOR_COMPACTION, scanInfo, 0, 0L, false, scanType); 356 this.matcher = CompactionScanQueryMatcher.create(scanInfo, scanType, Long.MAX_VALUE, 357 HConstants.OLDEST_TIMESTAMP, oldestUnexpiredTS, now, null, null, null); 358 seekAllScanner(scanInfo, scanners); 359 } 360 361 @VisibleForTesting 362 boolean isScanUsePread() { 363 return this.scanUsePread; 364 } 365 /** 366 * Seek the specified scanners with the given key 367 * @param scanners 368 * @param seekKey 369 * @param isLazy true if using lazy seek 370 * @param isParallelSeek true if using parallel seek 371 * @throws IOException 372 */ 373 protected void seekScanners(List<? extends KeyValueScanner> scanners, 374 Cell seekKey, boolean isLazy, boolean isParallelSeek) 375 throws IOException { 376 // Seek all scanners to the start of the Row (or if the exact matching row 377 // key does not exist, then to the start of the next matching Row). 378 // Always check bloom filter to optimize the top row seek for delete 379 // family marker. 380 if (isLazy) { 381 for (KeyValueScanner scanner : scanners) { 382 scanner.requestSeek(seekKey, false, true); 383 } 384 } else { 385 if (!isParallelSeek) { 386 long totalScannersSoughtBytes = 0; 387 for (KeyValueScanner scanner : scanners) { 388 if (matcher.isUserScan() && totalScannersSoughtBytes >= maxRowSize) { 389 throw new RowTooBigException("Max row size allowed: " + maxRowSize 390 + ", but row is bigger than that"); 391 } 392 scanner.seek(seekKey); 393 Cell c = scanner.peek(); 394 if (c != null) { 395 totalScannersSoughtBytes += PrivateCellUtil.estimatedSerializedSizeOf(c); 396 } 397 } 398 } else { 399 parallelSeek(scanners, seekKey); 400 } 401 } 402 } 403 404 protected void resetKVHeap(List<? extends KeyValueScanner> scanners, 405 CellComparator comparator) throws IOException { 406 // Combine all seeked scanners with a heap 407 heap = new KeyValueHeap(scanners, comparator); 408 } 409 410 /** 411 * Filters the given list of scanners using Bloom filter, time range, and TTL. 412 * <p> 413 * Will be overridden by testcase so declared as protected. 414 */ 415 @VisibleForTesting 416 protected List<KeyValueScanner> selectScannersFrom(HStore store, 417 List<? extends KeyValueScanner> allScanners) { 418 boolean memOnly; 419 boolean filesOnly; 420 if (scan instanceof InternalScan) { 421 InternalScan iscan = (InternalScan) scan; 422 memOnly = iscan.isCheckOnlyMemStore(); 423 filesOnly = iscan.isCheckOnlyStoreFiles(); 424 } else { 425 memOnly = false; 426 filesOnly = false; 427 } 428 429 List<KeyValueScanner> scanners = new ArrayList<>(allScanners.size()); 430 431 // We can only exclude store files based on TTL if minVersions is set to 0. 432 // Otherwise, we might have to return KVs that have technically expired. 433 long expiredTimestampCutoff = minVersions == 0 ? oldestUnexpiredTS : Long.MIN_VALUE; 434 435 // include only those scan files which pass all filters 436 for (KeyValueScanner kvs : allScanners) { 437 boolean isFile = kvs.isFileScanner(); 438 if ((!isFile && filesOnly) || (isFile && memOnly)) { 439 continue; 440 } 441 442 if (kvs.shouldUseScanner(scan, store, expiredTimestampCutoff)) { 443 scanners.add(kvs); 444 } else { 445 kvs.close(); 446 } 447 } 448 return scanners; 449 } 450 451 @Override 452 public Cell peek() { 453 return heap != null ? heap.peek() : null; 454 } 455 456 @Override 457 public KeyValue next() { 458 // throw runtime exception perhaps? 459 throw new RuntimeException("Never call StoreScanner.next()"); 460 } 461 462 @Override 463 public void close() { 464 close(true); 465 } 466 467 private void close(boolean withDelayedScannersClose) { 468 if (this.closing) { 469 return; 470 } 471 if (withDelayedScannersClose) { 472 this.closing = true; 473 } 474 // For mob compaction, we do not have a store. 475 if (this.store != null) { 476 this.store.deleteChangedReaderObserver(this); 477 } 478 if (withDelayedScannersClose) { 479 clearAndClose(scannersForDelayedClose); 480 clearAndClose(memStoreScannersAfterFlush); 481 clearAndClose(flushedstoreFileScanners); 482 if (this.heap != null) { 483 this.heap.close(); 484 this.currentScanners.clear(); 485 this.heap = null; // CLOSED! 486 } 487 } else { 488 if (this.heap != null) { 489 this.scannersForDelayedClose.add(this.heap); 490 this.currentScanners.clear(); 491 this.heap = null; 492 } 493 } 494 } 495 496 @Override 497 public boolean seek(Cell key) throws IOException { 498 if (checkFlushed()) { 499 reopenAfterFlush(); 500 } 501 return this.heap.seek(key); 502 } 503 504 /** 505 * Get the next row of values from this Store. 506 * @param outResult 507 * @param scannerContext 508 * @return true if there are more rows, false if scanner is done 509 */ 510 @Override 511 public boolean next(List<Cell> outResult, ScannerContext scannerContext) throws IOException { 512 if (scannerContext == null) { 513 throw new IllegalArgumentException("Scanner context cannot be null"); 514 } 515 if (checkFlushed() && reopenAfterFlush()) { 516 return scannerContext.setScannerState(NextState.MORE_VALUES).hasMoreValues(); 517 } 518 519 // if the heap was left null, then the scanners had previously run out anyways, close and 520 // return. 521 if (this.heap == null) { 522 // By this time partial close should happened because already heap is null 523 close(false);// Do all cleanup except heap.close() 524 return scannerContext.setScannerState(NextState.NO_MORE_VALUES).hasMoreValues(); 525 } 526 527 Cell cell = this.heap.peek(); 528 if (cell == null) { 529 close(false);// Do all cleanup except heap.close() 530 return scannerContext.setScannerState(NextState.NO_MORE_VALUES).hasMoreValues(); 531 } 532 533 // only call setRow if the row changes; avoids confusing the query matcher 534 // if scanning intra-row 535 536 // If no limits exists in the scope LimitScope.Between_Cells then we are sure we are changing 537 // rows. Else it is possible we are still traversing the same row so we must perform the row 538 // comparison. 539 if (!scannerContext.hasAnyLimit(LimitScope.BETWEEN_CELLS) || matcher.currentRow() == null) { 540 this.countPerRow = 0; 541 matcher.setToNewRow(cell); 542 } 543 544 // Clear progress away unless invoker has indicated it should be kept. 545 if (!scannerContext.getKeepProgress()) { 546 scannerContext.clearProgress(); 547 } 548 549 int count = 0; 550 long totalBytesRead = 0; 551 552 LOOP: do { 553 // Update and check the time limit based on the configured value of cellsPerTimeoutCheck 554 if ((kvsScanned % cellsPerHeartbeatCheck == 0)) { 555 if (scannerContext.checkTimeLimit(LimitScope.BETWEEN_CELLS)) { 556 return scannerContext.setScannerState(NextState.TIME_LIMIT_REACHED).hasMoreValues(); 557 } 558 } 559 // Do object compare - we set prevKV from the same heap. 560 if (prevCell != cell) { 561 ++kvsScanned; 562 } 563 checkScanOrder(prevCell, cell, comparator); 564 int cellSize = PrivateCellUtil.estimatedSerializedSizeOf(cell); 565 bytesRead += cellSize; 566 prevCell = cell; 567 scannerContext.setLastPeekedCell(cell); 568 topChanged = false; 569 ScanQueryMatcher.MatchCode qcode = matcher.match(cell); 570 switch (qcode) { 571 case INCLUDE: 572 case INCLUDE_AND_SEEK_NEXT_ROW: 573 case INCLUDE_AND_SEEK_NEXT_COL: 574 575 Filter f = matcher.getFilter(); 576 if (f != null) { 577 cell = f.transformCell(cell); 578 } 579 580 this.countPerRow++; 581 if (storeLimit > -1 && this.countPerRow > (storeLimit + storeOffset)) { 582 // do what SEEK_NEXT_ROW does. 583 if (!matcher.moreRowsMayExistAfter(cell)) { 584 close(false);// Do all cleanup except heap.close() 585 return scannerContext.setScannerState(NextState.NO_MORE_VALUES).hasMoreValues(); 586 } 587 matcher.clearCurrentRow(); 588 seekToNextRow(cell); 589 break LOOP; 590 } 591 592 // add to results only if we have skipped #storeOffset kvs 593 // also update metric accordingly 594 if (this.countPerRow > storeOffset) { 595 outResult.add(cell); 596 597 // Update local tracking information 598 count++; 599 totalBytesRead += cellSize; 600 601 // Update the progress of the scanner context 602 scannerContext.incrementSizeProgress(cellSize, 603 PrivateCellUtil.estimatedSizeOfCell(cell)); 604 scannerContext.incrementBatchProgress(1); 605 606 if (matcher.isUserScan() && totalBytesRead > maxRowSize) { 607 throw new RowTooBigException( 608 "Max row size allowed: " + maxRowSize + ", but the row is bigger than that."); 609 } 610 } 611 612 if (qcode == ScanQueryMatcher.MatchCode.INCLUDE_AND_SEEK_NEXT_ROW) { 613 if (!matcher.moreRowsMayExistAfter(cell)) { 614 close(false);// Do all cleanup except heap.close() 615 return scannerContext.setScannerState(NextState.NO_MORE_VALUES).hasMoreValues(); 616 } 617 matcher.clearCurrentRow(); 618 seekOrSkipToNextRow(cell); 619 } else if (qcode == ScanQueryMatcher.MatchCode.INCLUDE_AND_SEEK_NEXT_COL) { 620 seekOrSkipToNextColumn(cell); 621 } else { 622 this.heap.next(); 623 } 624 625 if (scannerContext.checkBatchLimit(LimitScope.BETWEEN_CELLS)) { 626 break LOOP; 627 } 628 if (scannerContext.checkSizeLimit(LimitScope.BETWEEN_CELLS)) { 629 break LOOP; 630 } 631 continue; 632 633 case DONE: 634 // Optimization for Gets! If DONE, no more to get on this row, early exit! 635 if (get) { 636 // Then no more to this row... exit. 637 close(false);// Do all cleanup except heap.close() 638 return scannerContext.setScannerState(NextState.NO_MORE_VALUES).hasMoreValues(); 639 } 640 matcher.clearCurrentRow(); 641 return scannerContext.setScannerState(NextState.MORE_VALUES).hasMoreValues(); 642 643 case DONE_SCAN: 644 close(false);// Do all cleanup except heap.close() 645 return scannerContext.setScannerState(NextState.NO_MORE_VALUES).hasMoreValues(); 646 647 case SEEK_NEXT_ROW: 648 // This is just a relatively simple end of scan fix, to short-cut end 649 // us if there is an endKey in the scan. 650 if (!matcher.moreRowsMayExistAfter(cell)) { 651 close(false);// Do all cleanup except heap.close() 652 return scannerContext.setScannerState(NextState.NO_MORE_VALUES).hasMoreValues(); 653 } 654 matcher.clearCurrentRow(); 655 seekOrSkipToNextRow(cell); 656 NextState stateAfterSeekNextRow = needToReturn(outResult); 657 if (stateAfterSeekNextRow != null) { 658 return scannerContext.setScannerState(stateAfterSeekNextRow).hasMoreValues(); 659 } 660 break; 661 662 case SEEK_NEXT_COL: 663 seekOrSkipToNextColumn(cell); 664 NextState stateAfterSeekNextColumn = needToReturn(outResult); 665 if (stateAfterSeekNextColumn != null) { 666 return scannerContext.setScannerState(stateAfterSeekNextColumn).hasMoreValues(); 667 } 668 break; 669 670 case SKIP: 671 this.heap.next(); 672 break; 673 674 case SEEK_NEXT_USING_HINT: 675 Cell nextKV = matcher.getNextKeyHint(cell); 676 if (nextKV != null) { 677 seekAsDirection(nextKV); 678 NextState stateAfterSeekByHint = needToReturn(outResult); 679 if (stateAfterSeekByHint != null) { 680 return scannerContext.setScannerState(stateAfterSeekByHint).hasMoreValues(); 681 } 682 } else { 683 heap.next(); 684 } 685 break; 686 687 default: 688 throw new RuntimeException("UNEXPECTED"); 689 } 690 } while ((cell = this.heap.peek()) != null); 691 692 if (count > 0) { 693 return scannerContext.setScannerState(NextState.MORE_VALUES).hasMoreValues(); 694 } 695 696 // No more keys 697 close(false);// Do all cleanup except heap.close() 698 return scannerContext.setScannerState(NextState.NO_MORE_VALUES).hasMoreValues(); 699 } 700 701 /** 702 * If the top cell won't be flushed into disk, the new top cell may be 703 * changed after #reopenAfterFlush. Because the older top cell only exist 704 * in the memstore scanner but the memstore scanner is replaced by hfile 705 * scanner after #reopenAfterFlush. If the row of top cell is changed, 706 * we should return the current cells. Otherwise, we may return 707 * the cells across different rows. 708 * @param outResult the cells which are visible for user scan 709 * @return null is the top cell doesn't change. Otherwise, the NextState 710 * to return 711 */ 712 private NextState needToReturn(List<Cell> outResult) { 713 if (!outResult.isEmpty() && topChanged) { 714 return heap.peek() == null ? NextState.NO_MORE_VALUES : NextState.MORE_VALUES; 715 } 716 return null; 717 } 718 719 private void seekOrSkipToNextRow(Cell cell) throws IOException { 720 // If it is a Get Scan, then we know that we are done with this row; there are no more 721 // rows beyond the current one: don't try to optimize. 722 if (!get) { 723 if (trySkipToNextRow(cell)) { 724 return; 725 } 726 } 727 seekToNextRow(cell); 728 } 729 730 private void seekOrSkipToNextColumn(Cell cell) throws IOException { 731 if (!trySkipToNextColumn(cell)) { 732 seekAsDirection(matcher.getKeyForNextColumn(cell)); 733 } 734 } 735 736 /** 737 * See if we should actually SEEK or rather just SKIP to the next Cell (see HBASE-13109). 738 * ScanQueryMatcher may issue SEEK hints, such as seek to next column, next row, 739 * or seek to an arbitrary seek key. This method decides whether a seek is the most efficient 740 * _actual_ way to get us to the requested cell (SEEKs are more expensive than SKIP, SKIP, 741 * SKIP inside the current, loaded block). 742 * It does this by looking at the next indexed key of the current HFile. This key 743 * is then compared with the _SEEK_ key, where a SEEK key is an artificial 'last possible key 744 * on the row' (only in here, we avoid actually creating a SEEK key; in the compare we work with 745 * the current Cell but compare as though it were a seek key; see down in 746 * matcher.compareKeyForNextRow, etc). If the compare gets us onto the 747 * next block we *_SEEK, otherwise we just SKIP to the next requested cell. 748 * 749 * <p>Other notes: 750 * <ul> 751 * <li>Rows can straddle block boundaries</li> 752 * <li>Versions of columns can straddle block boundaries (i.e. column C1 at T1 might be in a 753 * different block than column C1 at T2)</li> 754 * <li>We want to SKIP if the chance is high that we'll find the desired Cell after a 755 * few SKIPs...</li> 756 * <li>We want to SEEK when the chance is high that we'll be able to seek 757 * past many Cells, especially if we know we need to go to the next block.</li> 758 * </ul> 759 * <p>A good proxy (best effort) to determine whether SKIP is better than SEEK is whether 760 * we'll likely end up seeking to the next block (or past the next block) to get our next column. 761 * Example: 762 * <pre> 763 * | BLOCK 1 | BLOCK 2 | 764 * | r1/c1, r1/c2, r1/c3 | r1/c4, r1/c5, r2/c1 | 765 * ^ ^ 766 * | | 767 * Next Index Key SEEK_NEXT_ROW (before r2/c1) 768 * 769 * 770 * | BLOCK 1 | BLOCK 2 | 771 * | r1/c1/t5, r1/c1/t4, r1/c1/t3 | r1/c1/t2, r1/c1/T1, r1/c2/T3 | 772 * ^ ^ 773 * | | 774 * Next Index Key SEEK_NEXT_COL 775 * </pre> 776 * Now imagine we want columns c1 and c3 (see first diagram above), the 'Next Index Key' of r1/c4 777 * is > r1/c3 so we should seek to get to the c1 on the next row, r2. In second case, say we only 778 * want one version of c1, after we have it, a SEEK_COL will be issued to get to c2. Looking at 779 * the 'Next Index Key', it would land us in the next block, so we should SEEK. In other scenarios 780 * where the SEEK will not land us in the next block, it is very likely better to issues a series 781 * of SKIPs. 782 * @param cell current cell 783 * @return true means skip to next row, false means not 784 */ 785 @VisibleForTesting 786 protected boolean trySkipToNextRow(Cell cell) throws IOException { 787 Cell nextCell = null; 788 do { 789 Cell nextIndexedKey = getNextIndexedKey(); 790 if (nextIndexedKey != null && nextIndexedKey != KeyValueScanner.NO_NEXT_INDEXED_KEY 791 && matcher.compareKeyForNextRow(nextIndexedKey, cell) >= 0) { 792 this.heap.next(); 793 ++kvsScanned; 794 } else { 795 return false; 796 } 797 } while ((nextCell = this.heap.peek()) != null && CellUtil.matchingRows(cell, nextCell)); 798 return true; 799 } 800 801 /** 802 * See {@link org.apache.hadoop.hbase.regionserver.StoreScanner#trySkipToNextRow(Cell)} 803 * @param cell current cell 804 * @return true means skip to next column, false means not 805 */ 806 @VisibleForTesting 807 protected boolean trySkipToNextColumn(Cell cell) throws IOException { 808 Cell nextCell = null; 809 do { 810 Cell nextIndexedKey = getNextIndexedKey(); 811 if (nextIndexedKey != null && nextIndexedKey != KeyValueScanner.NO_NEXT_INDEXED_KEY 812 && matcher.compareKeyForNextColumn(nextIndexedKey, cell) >= 0) { 813 this.heap.next(); 814 ++kvsScanned; 815 } else { 816 return false; 817 } 818 } while ((nextCell = this.heap.peek()) != null && CellUtil.matchingRowColumn(cell, nextCell)); 819 // We need this check because it may happen that the new scanner that we get 820 // during heap.next() is requiring reseek due of fake KV previously generated for 821 // ROWCOL bloom filter optimization. See HBASE-19863 for more details 822 if (nextCell != null && matcher.compareKeyForNextColumn(nextCell, cell) < 0) { 823 return false; 824 } 825 return true; 826 } 827 828 @Override 829 public long getReadPoint() { 830 return this.readPt; 831 } 832 833 private static void clearAndClose(List<KeyValueScanner> scanners) { 834 for (KeyValueScanner s : scanners) { 835 s.close(); 836 } 837 scanners.clear(); 838 } 839 840 // Implementation of ChangedReadersObserver 841 @Override 842 public void updateReaders(List<HStoreFile> sfs, List<KeyValueScanner> memStoreScanners) 843 throws IOException { 844 if (CollectionUtils.isEmpty(sfs) && CollectionUtils.isEmpty(memStoreScanners)) { 845 return; 846 } 847 flushLock.lock(); 848 try { 849 flushed = true; 850 final boolean isCompaction = false; 851 boolean usePread = get || scanUsePread; 852 // SEE HBASE-19468 where the flushed files are getting compacted even before a scanner 853 // calls next(). So its better we create scanners here rather than next() call. Ensure 854 // these scanners are properly closed() whether or not the scan is completed successfully 855 // Eagerly creating scanners so that we have the ref counting ticking on the newly created 856 // store files. In case of stream scanners this eager creation does not induce performance 857 // penalty because in scans (that uses stream scanners) the next() call is bound to happen. 858 List<KeyValueScanner> scanners = store.getScanners(sfs, cacheBlocks, get, usePread, 859 isCompaction, matcher, scan.getStartRow(), scan.getStopRow(), this.readPt, false); 860 flushedstoreFileScanners.addAll(scanners); 861 if (!CollectionUtils.isEmpty(memStoreScanners)) { 862 clearAndClose(memStoreScannersAfterFlush); 863 memStoreScannersAfterFlush.addAll(memStoreScanners); 864 } 865 } finally { 866 flushLock.unlock(); 867 } 868 // Let the next() call handle re-creating and seeking 869 } 870 871 /** 872 * @return if top of heap has changed (and KeyValueHeap has to try the next KV) 873 */ 874 protected final boolean reopenAfterFlush() throws IOException { 875 // here we can make sure that we have a Store instance so no null check on store. 876 Cell lastTop = heap.peek(); 877 // When we have the scan object, should we not pass it to getScanners() to get a limited set of 878 // scanners? We did so in the constructor and we could have done it now by storing the scan 879 // object from the constructor 880 List<KeyValueScanner> scanners; 881 flushLock.lock(); 882 try { 883 List<KeyValueScanner> allScanners = 884 new ArrayList<>(flushedstoreFileScanners.size() + memStoreScannersAfterFlush.size()); 885 allScanners.addAll(flushedstoreFileScanners); 886 allScanners.addAll(memStoreScannersAfterFlush); 887 scanners = selectScannersFrom(store, allScanners); 888 // Clear the current set of flushed store files scanners so that they don't get added again 889 flushedstoreFileScanners.clear(); 890 memStoreScannersAfterFlush.clear(); 891 } finally { 892 flushLock.unlock(); 893 } 894 895 // Seek the new scanners to the last key 896 seekScanners(scanners, lastTop, false, parallelSeekEnabled); 897 // remove the older memstore scanner 898 for (int i = currentScanners.size() - 1; i >=0; i--) { 899 if (!currentScanners.get(i).isFileScanner()) { 900 scannersForDelayedClose.add(currentScanners.remove(i)); 901 } else { 902 // we add the memstore scanner to the end of currentScanners 903 break; 904 } 905 } 906 // add the newly created scanners on the flushed files and the current active memstore scanner 907 addCurrentScanners(scanners); 908 // Combine all seeked scanners with a heap 909 resetKVHeap(this.currentScanners, store.getComparator()); 910 resetQueryMatcher(lastTop); 911 if (heap.peek() == null || store.getComparator().compareRows(lastTop, this.heap.peek()) != 0) { 912 LOG.info("Storescanner.peek() is changed where before = " + lastTop.toString() + 913 ",and after = " + heap.peek()); 914 topChanged = true; 915 } else { 916 topChanged = false; 917 } 918 return topChanged; 919 } 920 921 private void resetQueryMatcher(Cell lastTopKey) { 922 // Reset the state of the Query Matcher and set to top row. 923 // Only reset and call setRow if the row changes; avoids confusing the 924 // query matcher if scanning intra-row. 925 Cell cell = heap.peek(); 926 if (cell == null) { 927 cell = lastTopKey; 928 } 929 if ((matcher.currentRow() == null) || !CellUtil.matchingRows(cell, matcher.currentRow())) { 930 this.countPerRow = 0; 931 // The setToNewRow will call reset internally 932 matcher.setToNewRow(cell); 933 } 934 } 935 936 /** 937 * Check whether scan as expected order 938 * @param prevKV 939 * @param kv 940 * @param comparator 941 * @throws IOException 942 */ 943 protected void checkScanOrder(Cell prevKV, Cell kv, 944 CellComparator comparator) throws IOException { 945 // Check that the heap gives us KVs in an increasing order. 946 assert prevKV == null || comparator == null 947 || comparator.compare(prevKV, kv) <= 0 : "Key " + prevKV 948 + " followed by a " + "smaller key " + kv + " in cf " + store; 949 } 950 951 protected boolean seekToNextRow(Cell c) throws IOException { 952 return reseek(PrivateCellUtil.createLastOnRow(c)); 953 } 954 955 /** 956 * Do a reseek in a normal StoreScanner(scan forward) 957 * @param kv 958 * @return true if scanner has values left, false if end of scanner 959 * @throws IOException 960 */ 961 protected boolean seekAsDirection(Cell kv) 962 throws IOException { 963 return reseek(kv); 964 } 965 966 @Override 967 public boolean reseek(Cell kv) throws IOException { 968 if (checkFlushed()) { 969 reopenAfterFlush(); 970 } 971 if (explicitColumnQuery && lazySeekEnabledGlobally) { 972 return heap.requestSeek(kv, true, useRowColBloom); 973 } 974 return heap.reseek(kv); 975 } 976 977 @VisibleForTesting 978 void trySwitchToStreamRead() { 979 if (readType != Scan.ReadType.DEFAULT || !scanUsePread || closing || 980 heap.peek() == null || bytesRead < preadMaxBytes) { 981 return; 982 } 983 LOG.debug("Switch to stream read (scanned={} bytes) of {}", bytesRead, 984 this.store.getColumnFamilyName()); 985 scanUsePread = false; 986 Cell lastTop = heap.peek(); 987 List<KeyValueScanner> memstoreScanners = new ArrayList<>(); 988 List<KeyValueScanner> scannersToClose = new ArrayList<>(); 989 for (KeyValueScanner kvs : currentScanners) { 990 if (!kvs.isFileScanner()) { 991 // collect memstorescanners here 992 memstoreScanners.add(kvs); 993 } else { 994 scannersToClose.add(kvs); 995 } 996 } 997 List<KeyValueScanner> fileScanners = null; 998 List<KeyValueScanner> newCurrentScanners; 999 KeyValueHeap newHeap; 1000 try { 1001 // We must have a store instance here so no null check 1002 // recreate the scanners on the current file scanners 1003 fileScanners = store.recreateScanners(scannersToClose, cacheBlocks, false, false, 1004 matcher, scan.getStartRow(), scan.includeStartRow(), scan.getStopRow(), 1005 scan.includeStopRow(), readPt, false); 1006 if (fileScanners == null) { 1007 return; 1008 } 1009 seekScanners(fileScanners, lastTop, false, parallelSeekEnabled); 1010 newCurrentScanners = new ArrayList<>(fileScanners.size() + memstoreScanners.size()); 1011 newCurrentScanners.addAll(fileScanners); 1012 newCurrentScanners.addAll(memstoreScanners); 1013 newHeap = new KeyValueHeap(newCurrentScanners, comparator); 1014 } catch (Exception e) { 1015 LOG.warn("failed to switch to stream read", e); 1016 if (fileScanners != null) { 1017 fileScanners.forEach(KeyValueScanner::close); 1018 } 1019 return; 1020 } 1021 currentScanners.clear(); 1022 addCurrentScanners(newCurrentScanners); 1023 this.heap = newHeap; 1024 resetQueryMatcher(lastTop); 1025 scannersToClose.forEach(KeyValueScanner::close); 1026 } 1027 1028 protected final boolean checkFlushed() { 1029 // check the var without any lock. Suppose even if we see the old 1030 // value here still it is ok to continue because we will not be resetting 1031 // the heap but will continue with the referenced memstore's snapshot. For compactions 1032 // any way we don't need the updateReaders at all to happen as we still continue with 1033 // the older files 1034 if (flushed) { 1035 // If there is a flush and the current scan is notified on the flush ensure that the 1036 // scan's heap gets reset and we do a seek on the newly flushed file. 1037 if (this.closing) { 1038 return false; 1039 } 1040 // reset the flag 1041 flushed = false; 1042 return true; 1043 } 1044 return false; 1045 } 1046 1047 /** 1048 * @see KeyValueScanner#getScannerOrder() 1049 */ 1050 @Override 1051 public long getScannerOrder() { 1052 return 0; 1053 } 1054 1055 /** 1056 * Seek storefiles in parallel to optimize IO latency as much as possible 1057 * @param scanners the list {@link KeyValueScanner}s to be read from 1058 * @param kv the KeyValue on which the operation is being requested 1059 * @throws IOException 1060 */ 1061 private void parallelSeek(final List<? extends KeyValueScanner> 1062 scanners, final Cell kv) throws IOException { 1063 if (scanners.isEmpty()) return; 1064 int storeFileScannerCount = scanners.size(); 1065 CountDownLatch latch = new CountDownLatch(storeFileScannerCount); 1066 List<ParallelSeekHandler> handlers = new ArrayList<>(storeFileScannerCount); 1067 for (KeyValueScanner scanner : scanners) { 1068 if (scanner instanceof StoreFileScanner) { 1069 ParallelSeekHandler seekHandler = new ParallelSeekHandler(scanner, kv, 1070 this.readPt, latch); 1071 executor.submit(seekHandler); 1072 handlers.add(seekHandler); 1073 } else { 1074 scanner.seek(kv); 1075 latch.countDown(); 1076 } 1077 } 1078 1079 try { 1080 latch.await(); 1081 } catch (InterruptedException ie) { 1082 throw (InterruptedIOException)new InterruptedIOException().initCause(ie); 1083 } 1084 1085 for (ParallelSeekHandler handler : handlers) { 1086 if (handler.getErr() != null) { 1087 throw new IOException(handler.getErr()); 1088 } 1089 } 1090 } 1091 1092 /** 1093 * Used in testing. 1094 * @return all scanners in no particular order 1095 */ 1096 @VisibleForTesting 1097 List<KeyValueScanner> getAllScannersForTesting() { 1098 List<KeyValueScanner> allScanners = new ArrayList<>(); 1099 KeyValueScanner current = heap.getCurrentForTesting(); 1100 if (current != null) 1101 allScanners.add(current); 1102 for (KeyValueScanner scanner : heap.getHeap()) 1103 allScanners.add(scanner); 1104 return allScanners; 1105 } 1106 1107 static void enableLazySeekGlobally(boolean enable) { 1108 lazySeekEnabledGlobally = enable; 1109 } 1110 1111 /** 1112 * @return The estimated number of KVs seen by this scanner (includes some skipped KVs). 1113 */ 1114 public long getEstimatedNumberOfKvsScanned() { 1115 return this.kvsScanned; 1116 } 1117 1118 @Override 1119 public Cell getNextIndexedKey() { 1120 return this.heap.getNextIndexedKey(); 1121 } 1122 1123 @Override 1124 public void shipped() throws IOException { 1125 if (prevCell != null) { 1126 // Do the copy here so that in case the prevCell ref is pointing to the previous 1127 // blocks we can safely release those blocks. 1128 // This applies to blocks that are got from Bucket cache, L1 cache and the blocks 1129 // fetched from HDFS. Copying this would ensure that we let go the references to these 1130 // blocks so that they can be GCed safely(in case of bucket cache) 1131 prevCell = KeyValueUtil.toNewKeyCell(this.prevCell); 1132 } 1133 matcher.beforeShipped(); 1134 // There wont be further fetch of Cells from these scanners. Just close. 1135 clearAndClose(scannersForDelayedClose); 1136 if (this.heap != null) { 1137 this.heap.shipped(); 1138 // When switching from pread to stream, we will open a new scanner for each store file, but 1139 // the old scanner may still track the HFileBlocks we have scanned but not sent back to client 1140 // yet. If we close the scanner immediately then the HFileBlocks may be messed up by others 1141 // before we serialize and send it back to client. The HFileBlocks will be released in shipped 1142 // method, so we here will also open new scanners and close old scanners in shipped method. 1143 // See HBASE-18055 for more details. 1144 trySwitchToStreamRead(); 1145 } 1146 } 1147} 1148