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