View Javadoc

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