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.io.hfile;
019
020import static org.apache.hadoop.hbase.trace.HBaseSemanticAttributes.BLOCK_CACHE_KEY_KEY;
021
022import io.opentelemetry.api.common.Attributes;
023import io.opentelemetry.api.trace.Span;
024import java.io.DataInput;
025import java.io.IOException;
026import java.nio.ByteBuffer;
027import java.util.ArrayList;
028import java.util.Optional;
029import java.util.function.IntConsumer;
030import org.apache.hadoop.conf.Configurable;
031import org.apache.hadoop.conf.Configuration;
032import org.apache.hadoop.fs.Path;
033import org.apache.hadoop.hbase.ByteBufferKeyOnlyKeyValue;
034import org.apache.hadoop.hbase.Cell;
035import org.apache.hadoop.hbase.CellComparator;
036import org.apache.hadoop.hbase.CellUtil;
037import org.apache.hadoop.hbase.ExtendedCell;
038import org.apache.hadoop.hbase.HBaseInterfaceAudience;
039import org.apache.hadoop.hbase.HConstants;
040import org.apache.hadoop.hbase.KeyValue;
041import org.apache.hadoop.hbase.PrivateCellUtil;
042import org.apache.hadoop.hbase.SizeCachedByteBufferKeyValue;
043import org.apache.hadoop.hbase.SizeCachedKeyValue;
044import org.apache.hadoop.hbase.SizeCachedNoTagsByteBufferKeyValue;
045import org.apache.hadoop.hbase.SizeCachedNoTagsKeyValue;
046import org.apache.hadoop.hbase.io.compress.Compression;
047import org.apache.hadoop.hbase.io.encoding.DataBlockEncoder;
048import org.apache.hadoop.hbase.io.encoding.DataBlockEncoding;
049import org.apache.hadoop.hbase.io.encoding.HFileBlockDecodingContext;
050import org.apache.hadoop.hbase.monitoring.ThreadLocalServerSideScanMetrics;
051import org.apache.hadoop.hbase.nio.ByteBuff;
052import org.apache.hadoop.hbase.regionserver.KeyValueScanner;
053import org.apache.hadoop.hbase.util.ByteBufferUtils;
054import org.apache.hadoop.hbase.util.Bytes;
055import org.apache.hadoop.hbase.util.HFileArchiveUtil;
056import org.apache.hadoop.hbase.util.IdLock;
057import org.apache.hadoop.hbase.util.ObjectIntPair;
058import org.apache.hadoop.io.WritableUtils;
059import org.apache.yetus.audience.InterfaceAudience;
060import org.slf4j.Logger;
061import org.slf4j.LoggerFactory;
062
063/**
064 * Implementation that can handle all hfile versions of {@link HFile.Reader}.
065 */
066@InterfaceAudience.Private
067@edu.umd.cs.findbugs.annotations.SuppressWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
068public abstract class HFileReaderImpl implements HFile.Reader, Configurable {
069  // This class is HFileReaderV3 + HFileReaderV2 + AbstractHFileReader all squashed together into
070  // one file. Ditto for all the HFileReader.ScannerV? implementations. I was running up against
071  // the MaxInlineLevel limit because too many tiers involved reading from an hfile. Was also hard
072  // to navigate the source code when so many classes participating in read.
073  private static final Logger LOG = LoggerFactory.getLogger(HFileReaderImpl.class);
074
075  /** Data block index reader keeping the root data index in memory */
076  protected HFileBlockIndex.CellBasedKeyBlockIndexReader dataBlockIndexReader;
077
078  /** Meta block index reader -- always single level */
079  protected HFileBlockIndex.ByteArrayKeyBlockIndexReader metaBlockIndexReader;
080
081  protected FixedFileTrailer trailer;
082
083  private final boolean primaryReplicaReader;
084
085  /**
086   * What kind of data block encoding should be used while reading, writing, and handling cache.
087   */
088  protected HFileDataBlockEncoder dataBlockEncoder = NoOpDataBlockEncoder.INSTANCE;
089
090  /** Block cache configuration. */
091  protected final CacheConfig cacheConf;
092
093  protected ReaderContext context;
094
095  protected final HFileInfo fileInfo;
096
097  /** Path of file */
098  protected final Path path;
099
100  /** File name to be used for block names */
101  protected final String name;
102
103  private Configuration conf;
104
105  protected HFileContext hfileContext;
106
107  /** Filesystem-level block reader. */
108  protected HFileBlock.FSReader fsBlockReader;
109
110  /**
111   * A "sparse lock" implementation allowing to lock on a particular block identified by offset. The
112   * purpose of this is to avoid two clients loading the same block, and have all but one client
113   * wait to get the block from the cache.
114   */
115  private IdLock offsetLock = new IdLock();
116
117  /** Minimum minor version supported by this HFile format */
118  static final int MIN_MINOR_VERSION = 0;
119
120  /** Maximum minor version supported by this HFile format */
121  // We went to version 2 when we moved to pb'ing fileinfo and the trailer on
122  // the file. This version can read Writables version 1.
123  static final int MAX_MINOR_VERSION = 3;
124
125  /** Minor versions starting with this number have faked index key */
126  static final int MINOR_VERSION_WITH_FAKED_KEY = 3;
127
128  private final String fileName;
129
130  private final String region;
131
132  private final String family;
133
134  private final boolean archived;
135
136  /**
137   * Opens a HFile.
138   * @param context   Reader context info
139   * @param fileInfo  HFile info
140   * @param cacheConf Cache configuration.
141   * @param conf      Configuration
142   */
143  @edu.umd.cs.findbugs.annotations.SuppressWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
144  public HFileReaderImpl(ReaderContext context, HFileInfo fileInfo, CacheConfig cacheConf,
145    Configuration conf) throws IOException {
146    this.cacheConf = cacheConf;
147    this.context = context;
148    this.path = context.getFilePath();
149    this.name = path.getName();
150    this.conf = conf;
151    this.primaryReplicaReader = context.isPrimaryReplicaReader();
152    this.fileInfo = fileInfo;
153    this.trailer = fileInfo.getTrailer();
154    this.hfileContext = fileInfo.getHFileContext();
155    this.fsBlockReader =
156      new HFileBlock.FSReaderImpl(context, hfileContext, cacheConf.getByteBuffAllocator(), conf);
157    this.dataBlockEncoder = HFileDataBlockEncoderImpl.createFromFileInfo(fileInfo);
158    fsBlockReader.setDataBlockEncoder(dataBlockEncoder, conf);
159    dataBlockIndexReader = fileInfo.getDataBlockIndexReader();
160    metaBlockIndexReader = fileInfo.getMetaBlockIndexReader();
161    fileName = path.getName();
162    family = path.getParent().getName();
163    region = path.getParent().getParent().getName();
164    archived = HFileArchiveUtil.isHFileArchived(path);
165  }
166
167  @SuppressWarnings("serial")
168  public static class BlockIndexNotLoadedException extends IllegalStateException {
169    public BlockIndexNotLoadedException(Path path) {
170      // Add a message in case anyone relies on it as opposed to class name.
171      super(path + " block index not loaded");
172    }
173  }
174
175  public CacheConfig getCacheConf() {
176    return cacheConf;
177  }
178
179  private Optional<String> toStringFirstKey() {
180    return getFirstKey().map(CellUtil::getCellKeyAsString);
181  }
182
183  private Optional<String> toStringLastKey() {
184    return getLastKey().map(CellUtil::getCellKeyAsString);
185  }
186
187  @Override
188  public String toString() {
189    return "reader=" + path.toString()
190      + (!isFileInfoLoaded()
191        ? ""
192        : ", compression=" + trailer.getCompressionCodec().getName() + ", cacheConf=" + cacheConf
193          + ", firstKey=" + toStringFirstKey() + ", lastKey=" + toStringLastKey())
194      + ", avgKeyLen=" + fileInfo.getAvgKeyLen() + ", avgValueLen=" + fileInfo.getAvgValueLen()
195      + ", entries=" + trailer.getEntryCount() + ", length=" + context.getFileSize();
196  }
197
198  @Override
199  public long length() {
200    return context.getFileSize();
201  }
202
203  /**
204   * @return the first key in the file. May be null if file has no entries. Note that this is not
205   *         the first row key, but rather the byte form of the first KeyValue.
206   */
207  @Override
208  public Optional<ExtendedCell> getFirstKey() {
209    if (dataBlockIndexReader == null) {
210      throw new BlockIndexNotLoadedException(path);
211    }
212    return dataBlockIndexReader.isEmpty()
213      ? Optional.empty()
214      : Optional.of(dataBlockIndexReader.getRootBlockKey(0));
215  }
216
217  /**
218   * TODO left from {@link HFile} version 1: move this to StoreFile after Ryan's patch goes in to
219   * eliminate {@link KeyValue} here.
220   * @return the first row key, or null if the file is empty.
221   */
222  @Override
223  public Optional<byte[]> getFirstRowKey() {
224    // We have to copy the row part to form the row key alone
225    return getFirstKey().map(CellUtil::cloneRow);
226  }
227
228  /**
229   * TODO left from {@link HFile} version 1: move this to StoreFile after Ryan's patch goes in to
230   * eliminate {@link KeyValue} here.
231   * @return the last row key, or null if the file is empty.
232   */
233  @Override
234  public Optional<byte[]> getLastRowKey() {
235    // We have to copy the row part to form the row key alone
236    return getLastKey().map(CellUtil::cloneRow);
237  }
238
239  /** Returns number of KV entries in this HFile */
240  @Override
241  public long getEntries() {
242    return trailer.getEntryCount();
243  }
244
245  /** Returns comparator */
246  @Override
247  public CellComparator getComparator() {
248    return this.hfileContext.getCellComparator();
249  }
250
251  public Compression.Algorithm getCompressionAlgorithm() {
252    return trailer.getCompressionCodec();
253  }
254
255  /**
256   * @return the total heap size of data and meta block indexes in bytes. Does not take into account
257   *         non-root blocks of a multilevel data index.
258   */
259  @Override
260  public long indexSize() {
261    return (dataBlockIndexReader != null ? dataBlockIndexReader.heapSize() : 0)
262      + ((metaBlockIndexReader != null) ? metaBlockIndexReader.heapSize() : 0);
263  }
264
265  @Override
266  public String getName() {
267    return name;
268  }
269
270  @Override
271  public void setDataBlockEncoder(HFileDataBlockEncoder dataBlockEncoder) {
272    this.dataBlockEncoder = dataBlockEncoder;
273    this.fsBlockReader.setDataBlockEncoder(dataBlockEncoder, conf);
274  }
275
276  @Override
277  public void setDataBlockIndexReader(HFileBlockIndex.CellBasedKeyBlockIndexReader reader) {
278    this.dataBlockIndexReader = reader;
279  }
280
281  @Override
282  public HFileBlockIndex.CellBasedKeyBlockIndexReader getDataBlockIndexReader() {
283    return dataBlockIndexReader;
284  }
285
286  @Override
287  public void setMetaBlockIndexReader(HFileBlockIndex.ByteArrayKeyBlockIndexReader reader) {
288    this.metaBlockIndexReader = reader;
289  }
290
291  @Override
292  public HFileBlockIndex.ByteArrayKeyBlockIndexReader getMetaBlockIndexReader() {
293    return metaBlockIndexReader;
294  }
295
296  @Override
297  public FixedFileTrailer getTrailer() {
298    return trailer;
299  }
300
301  @Override
302  public ReaderContext getContext() {
303    return this.context;
304  }
305
306  @Override
307  public HFileInfo getHFileInfo() {
308    return this.fileInfo;
309  }
310
311  @Override
312  public boolean isPrimaryReplicaReader() {
313    return primaryReplicaReader;
314  }
315
316  /**
317   * An exception thrown when an operation requiring a scanner to be seeked is invoked on a scanner
318   * that is not seeked.
319   */
320  @SuppressWarnings("serial")
321  public static class NotSeekedException extends IllegalStateException {
322    public NotSeekedException(Path path) {
323      super(path + " not seeked to a key/value");
324    }
325  }
326
327  public static class HFileScannerImpl implements HFileScanner {
328    private ByteBuff blockBuffer;
329    protected final boolean cacheBlocks;
330    protected final boolean pread;
331    protected final boolean isCompaction;
332    private int currKeyLen;
333    private int currValueLen;
334    private int currMemstoreTSLen;
335    private long currMemstoreTS;
336    protected final HFile.Reader reader;
337    private int currTagsLen;
338    private short rowLen;
339    // buffer backed keyonlyKV
340    private ByteBufferKeyOnlyKeyValue bufBackedKeyOnlyKv = new ByteBufferKeyOnlyKeyValue();
341    // A pair for reusing in blockSeek() so that we don't garbage lot of objects
342    final ObjectIntPair<ByteBuffer> pair = new ObjectIntPair<>();
343
344    /**
345     * The next indexed key is to keep track of the indexed key of the next data block. If the
346     * nextIndexedKey is HConstants.NO_NEXT_INDEXED_KEY, it means that the current data block is the
347     * last data block. If the nextIndexedKey is null, it means the nextIndexedKey has not been
348     * loaded yet.
349     */
350    protected ExtendedCell nextIndexedKey;
351
352    // Current block being used. NOTICE: DON't release curBlock separately except in shipped() or
353    // close() methods. Because the shipped() or close() will do the release finally, even if any
354    // exception occur the curBlock will be released by the close() method (see
355    // RegionScannerImpl#handleException). Call the releaseIfNotCurBlock() to release the
356    // unreferenced block please.
357    protected HFileBlock curBlock;
358    // Whether we returned a result for curBlock's size in recordBlockSize().
359    // gets reset whenever curBlock is changed.
360    private boolean providedCurrentBlockSize = false;
361
362    public HFileBlock getCurBlock() {
363      return curBlock;
364    }
365
366    // Previous blocks that were used in the course of the read
367    protected final ArrayList<HFileBlock> prevBlocks = new ArrayList<>();
368
369    public HFileScannerImpl(final HFile.Reader reader, final boolean cacheBlocks,
370      final boolean pread, final boolean isCompaction) {
371      this.reader = reader;
372      this.cacheBlocks = cacheBlocks;
373      this.pread = pread;
374      this.isCompaction = isCompaction;
375    }
376
377    void updateCurrBlockRef(HFileBlock block) {
378      if (block != null && curBlock != null && block.getOffset() == curBlock.getOffset()) {
379        return;
380      }
381      if (this.curBlock != null && this.curBlock.isSharedMem()) {
382        prevBlocks.add(this.curBlock);
383      }
384      this.curBlock = block;
385      this.providedCurrentBlockSize = false;
386    }
387
388    void reset() {
389      // We don't have to keep ref to heap block
390      if (this.curBlock != null && this.curBlock.isSharedMem()) {
391        this.prevBlocks.add(this.curBlock);
392      }
393      this.curBlock = null;
394    }
395
396    private void returnBlocks(boolean returnAll) {
397      this.prevBlocks.forEach(HFileBlock::release);
398      this.prevBlocks.clear();
399      if (returnAll && this.curBlock != null) {
400        this.curBlock.release();
401        this.curBlock = null;
402      }
403    }
404
405    @Override
406    public boolean isSeeked() {
407      return blockBuffer != null;
408    }
409
410    @Override
411    public String toString() {
412      return "HFileScanner for reader " + String.valueOf(getReader());
413    }
414
415    protected void assertSeeked() {
416      if (!isSeeked()) {
417        throw new NotSeekedException(reader.getPath());
418      }
419    }
420
421    @Override
422    public HFile.Reader getReader() {
423      return reader;
424    }
425
426    // From non encoded HFiles, we always read back KeyValue or its descendant.(Note: When HFile
427    // block is in DBB, it will be OffheapKV). So all parts of the Cell is in a contiguous
428    // array/buffer. How many bytes we should wrap to make the KV is what this method returns.
429    private int getKVBufSize() {
430      int kvBufSize = KEY_VALUE_LEN_SIZE + currKeyLen + currValueLen;
431      if (currTagsLen > 0) {
432        kvBufSize += Bytes.SIZEOF_SHORT + currTagsLen;
433      }
434      return kvBufSize;
435    }
436
437    @Override
438    public void close() {
439      if (!pread) {
440        // For seek + pread stream socket should be closed when the scanner is closed. HBASE-9393
441        reader.unbufferStream();
442      }
443      this.returnBlocks(true);
444    }
445
446    @Override
447    public void recordBlockSize(IntConsumer blockSizeConsumer) {
448      if (!providedCurrentBlockSize && curBlock != null) {
449        providedCurrentBlockSize = true;
450        blockSizeConsumer.accept(curBlock.getUncompressedSizeWithoutHeader());
451      }
452    }
453
454    // Returns the #bytes in HFile for the current cell. Used to skip these many bytes in current
455    // HFile block's buffer so as to position to the next cell.
456    private int getCurCellSerializedSize() {
457      int curCellSize = KEY_VALUE_LEN_SIZE + currKeyLen + currValueLen + currMemstoreTSLen;
458      if (this.reader.getFileContext().isIncludesTags()) {
459        curCellSize += Bytes.SIZEOF_SHORT + currTagsLen;
460      }
461      return curCellSize;
462    }
463
464    protected void readKeyValueLen() {
465      // This is a hot method. We go out of our way to make this method short so it can be
466      // inlined and is not too big to compile. We also manage position in ByteBuffer ourselves
467      // because it is faster than going via range-checked ByteBuffer methods or going through a
468      // byte buffer array a byte at a time.
469      // Get a long at a time rather than read two individual ints. In micro-benchmarking, even
470      // with the extra bit-fiddling, this is order-of-magnitude faster than getting two ints.
471      // Trying to imitate what was done - need to profile if this is better or
472      // earlier way is better by doing mark and reset?
473      // But ensure that you read long instead of two ints
474      long ll = blockBuffer.getLongAfterPosition(0);
475      // Read top half as an int of key length and bottom int as value length
476      this.currKeyLen = (int) (ll >> Integer.SIZE);
477      this.currValueLen = (int) (Bytes.MASK_FOR_LOWER_INT_IN_LONG ^ ll);
478      checkKeyValueLen();
479      this.rowLen = blockBuffer.getShortAfterPosition(Bytes.SIZEOF_LONG);
480      // Move position past the key and value lengths and then beyond the key and value
481      int p = (Bytes.SIZEOF_LONG + currKeyLen + currValueLen);
482      if (reader.getFileContext().isIncludesTags()) {
483        // Tags length is a short.
484        this.currTagsLen = blockBuffer.getShortAfterPosition(p);
485        checkTagsLen();
486        p += (Bytes.SIZEOF_SHORT + currTagsLen);
487      }
488      readMvccVersion(p);
489    }
490
491    private final void checkTagsLen() {
492      if (checkLen(this.currTagsLen)) {
493        throw new IllegalStateException(
494          "Invalid currTagsLen " + this.currTagsLen + ". Block offset: " + curBlock.getOffset()
495            + ", block length: " + this.blockBuffer.limit() + ", position: "
496            + this.blockBuffer.position() + " (without header)." + " path=" + reader.getPath());
497      }
498    }
499
500    /**
501     * Read mvcc. Does checks to see if we even need to read the mvcc at all.
502     */
503    protected void readMvccVersion(final int offsetFromPos) {
504      // See if we even need to decode mvcc.
505      if (!this.reader.getHFileInfo().shouldIncludeMemStoreTS()) {
506        return;
507      }
508      if (!this.reader.getHFileInfo().isDecodeMemstoreTS()) {
509        currMemstoreTS = 0;
510        currMemstoreTSLen = 1;
511        return;
512      }
513      _readMvccVersion(offsetFromPos);
514    }
515
516    /**
517     * Actually do the mvcc read. Does no checks.
518     */
519    private void _readMvccVersion(int offsetFromPos) {
520      // This is Bytes#bytesToVint inlined so can save a few instructions in this hot method; i.e.
521      // previous if one-byte vint, we'd redo the vint call to find int size.
522      // Also the method is kept small so can be inlined.
523      byte firstByte = blockBuffer.getByteAfterPosition(offsetFromPos);
524      int len = WritableUtils.decodeVIntSize(firstByte);
525      if (len == 1) {
526        this.currMemstoreTS = firstByte;
527      } else {
528        int remaining = len - 1;
529        long i = 0;
530        offsetFromPos++;
531        if (remaining >= Bytes.SIZEOF_INT) {
532          // The int read has to be converted to unsigned long so the & op
533          i = (blockBuffer.getIntAfterPosition(offsetFromPos) & 0x00000000ffffffffL);
534          remaining -= Bytes.SIZEOF_INT;
535          offsetFromPos += Bytes.SIZEOF_INT;
536        }
537        if (remaining >= Bytes.SIZEOF_SHORT) {
538          short s = blockBuffer.getShortAfterPosition(offsetFromPos);
539          i = i << 16;
540          i = i | (s & 0xFFFF);
541          remaining -= Bytes.SIZEOF_SHORT;
542          offsetFromPos += Bytes.SIZEOF_SHORT;
543        }
544        for (int idx = 0; idx < remaining; idx++) {
545          byte b = blockBuffer.getByteAfterPosition(offsetFromPos + idx);
546          i = i << 8;
547          i = i | (b & 0xFF);
548        }
549        currMemstoreTS = (WritableUtils.isNegativeVInt(firstByte) ? ~i : i);
550      }
551      this.currMemstoreTSLen = len;
552    }
553
554    /**
555     * Within a loaded block, seek looking for the last key that is smaller than (or equal to?) the
556     * key we are interested in. A note on the seekBefore: if you have seekBefore = true, AND the
557     * first key in the block = key, then you'll get thrown exceptions. The caller has to check for
558     * that case and load the previous block as appropriate. the key to find find the key before the
559     * given key in case of exact match.
560     * @return 0 in case of an exact key match, 1 in case of an inexact match, -2 in case of an
561     *         inexact match and furthermore, the input key less than the first key of current
562     *         block(e.g. using a faked index key)
563     */
564    protected int blockSeek(Cell key, boolean seekBefore) {
565      int klen, vlen, tlen = 0;
566      int lastKeyValueSize = -1;
567      int offsetFromPos;
568      do {
569        offsetFromPos = 0;
570        // Better to ensure that we use the BB Utils here
571        long ll = blockBuffer.getLongAfterPosition(offsetFromPos);
572        klen = (int) (ll >> Integer.SIZE);
573        vlen = (int) (Bytes.MASK_FOR_LOWER_INT_IN_LONG ^ ll);
574        if (checkKeyLen(klen) || checkLen(vlen)) {
575          throw new IllegalStateException(
576            "Invalid klen " + klen + " or vlen " + vlen + ". Block offset: " + curBlock.getOffset()
577              + ", block length: " + blockBuffer.limit() + ", position: " + blockBuffer.position()
578              + " (without header)." + " path=" + reader.getPath());
579        }
580        offsetFromPos += Bytes.SIZEOF_LONG;
581        this.rowLen = blockBuffer.getShortAfterPosition(offsetFromPos);
582        blockBuffer.asSubByteBuffer(blockBuffer.position() + offsetFromPos, klen, pair);
583        bufBackedKeyOnlyKv.setKey(pair.getFirst(), pair.getSecond(), klen, rowLen);
584        int comp =
585          PrivateCellUtil.compareKeyIgnoresMvcc(reader.getComparator(), key, bufBackedKeyOnlyKv);
586        offsetFromPos += klen + vlen;
587        if (this.reader.getFileContext().isIncludesTags()) {
588          // Read short as unsigned, high byte first
589          tlen = ((blockBuffer.getByteAfterPosition(offsetFromPos) & 0xff) << 8)
590            ^ (blockBuffer.getByteAfterPosition(offsetFromPos + 1) & 0xff);
591          if (checkLen(tlen)) {
592            throw new IllegalStateException("Invalid tlen " + tlen + ". Block offset: "
593              + curBlock.getOffset() + ", block length: " + blockBuffer.limit() + ", position: "
594              + blockBuffer.position() + " (without header)." + " path=" + reader.getPath());
595          }
596          // add the two bytes read for the tags.
597          offsetFromPos += tlen + (Bytes.SIZEOF_SHORT);
598        }
599        if (this.reader.getHFileInfo().shouldIncludeMemStoreTS()) {
600          // Directly read the mvcc based on current position
601          readMvccVersion(offsetFromPos);
602        }
603        if (comp == 0) {
604          if (seekBefore) {
605            if (lastKeyValueSize < 0) {
606              throw new IllegalStateException("blockSeek with seekBefore "
607                + "at the first key of the block: key=" + CellUtil.getCellKeyAsString(key)
608                + ", blockOffset=" + curBlock.getOffset() + ", onDiskSize="
609                + curBlock.getOnDiskSizeWithHeader() + ", path=" + reader.getPath());
610            }
611            blockBuffer.moveBack(lastKeyValueSize);
612            readKeyValueLen();
613            return 1; // non exact match.
614          }
615          currKeyLen = klen;
616          currValueLen = vlen;
617          currTagsLen = tlen;
618          return 0; // indicate exact match
619        } else if (comp < 0) {
620          if (lastKeyValueSize > 0) {
621            blockBuffer.moveBack(lastKeyValueSize);
622          }
623          readKeyValueLen();
624          if (lastKeyValueSize == -1 && blockBuffer.position() == 0) {
625            return HConstants.INDEX_KEY_MAGIC;
626          }
627          return 1;
628        }
629        // The size of this key/value tuple, including key/value length fields.
630        lastKeyValueSize = klen + vlen + currMemstoreTSLen + KEY_VALUE_LEN_SIZE;
631        // include tag length also if tags included with KV
632        if (reader.getFileContext().isIncludesTags()) {
633          lastKeyValueSize += tlen + Bytes.SIZEOF_SHORT;
634        }
635        blockBuffer.skip(lastKeyValueSize);
636      } while (blockBuffer.hasRemaining());
637
638      // Seek to the last key we successfully read. This will happen if this is
639      // the last key/value pair in the file, in which case the following call
640      // to next() has to return false.
641      blockBuffer.moveBack(lastKeyValueSize);
642      readKeyValueLen();
643      return 1; // didn't exactly find it.
644    }
645
646    @Override
647    public ExtendedCell getNextIndexedKey() {
648      return nextIndexedKey;
649    }
650
651    @Override
652    public int seekTo(ExtendedCell key) throws IOException {
653      return seekTo(key, true);
654    }
655
656    @Override
657    public int reseekTo(ExtendedCell key) throws IOException {
658      int compared;
659      if (isSeeked()) {
660        compared = compareKey(reader.getComparator(), key);
661        if (compared < 1) {
662          // If the required key is less than or equal to current key, then
663          // don't do anything.
664          return compared;
665        } else {
666          // The comparison with no_next_index_key has to be checked
667          if (
668            this.nextIndexedKey != null && (this.nextIndexedKey
669                == KeyValueScanner.NO_NEXT_INDEXED_KEY
670              || PrivateCellUtil.compareKeyIgnoresMvcc(reader.getComparator(), key, nextIndexedKey)
671                  < 0)
672          ) {
673            // The reader shall continue to scan the current data block instead
674            // of querying the
675            // block index as long as it knows the target key is strictly
676            // smaller than
677            // the next indexed key or the current data block is the last data
678            // block.
679            return loadBlockAndSeekToKey(this.curBlock, nextIndexedKey, false, key, false);
680          }
681        }
682      }
683      // Don't rewind on a reseek operation, because reseek implies that we are
684      // always going forward in the file.
685      return seekTo(key, false);
686    }
687
688    /**
689     * An internal API function. Seek to the given key, optionally rewinding to the first key of the
690     * block before doing the seek.
691     * @param key    - a cell representing the key that we need to fetch
692     * @param rewind whether to rewind to the first key of the block before doing the seek. If this
693     *               is false, we are assuming we never go back, otherwise the result is undefined.
694     * @return -1 if the key is earlier than the first key of the file, 0 if we are at the given
695     *         key, 1 if we are past the given key -2 if the key is earlier than the first key of
696     *         the file while using a faked index key
697     */
698    public int seekTo(ExtendedCell key, boolean rewind) throws IOException {
699      HFileBlockIndex.BlockIndexReader indexReader = reader.getDataBlockIndexReader();
700      BlockWithScanInfo blockWithScanInfo = indexReader.loadDataBlockWithScanInfo(key, curBlock,
701        cacheBlocks, pread, isCompaction, getEffectiveDataBlockEncoding(), reader);
702      if (blockWithScanInfo == null || blockWithScanInfo.getHFileBlock() == null) {
703        // This happens if the key e.g. falls before the beginning of the file.
704        return -1;
705      }
706      return loadBlockAndSeekToKey(blockWithScanInfo.getHFileBlock(),
707        blockWithScanInfo.getNextIndexedKey(), rewind, key, false);
708    }
709
710    @Override
711    public boolean seekBefore(ExtendedCell key) throws IOException {
712      HFileBlock seekToBlock = reader.getDataBlockIndexReader().seekToDataBlock(key, curBlock,
713        cacheBlocks, pread, isCompaction, reader.getEffectiveEncodingInCache(isCompaction), reader);
714      if (seekToBlock == null) {
715        return false;
716      }
717      ExtendedCell firstKey = getFirstKeyCellInBlock(seekToBlock);
718      if (PrivateCellUtil.compareKeyIgnoresMvcc(reader.getComparator(), firstKey, key) >= 0) {
719        long previousBlockOffset = seekToBlock.getPrevBlockOffset();
720        // The key we are interested in
721        if (previousBlockOffset == -1) {
722          // we have a 'problem', the key we want is the first of the file.
723          releaseIfNotCurBlock(seekToBlock);
724          return false;
725        }
726
727        // The first key in the current block 'seekToBlock' is greater than the given
728        // seekBefore key. We will go ahead by reading the next block that satisfies the
729        // given key. Return the current block before reading the next one.
730        releaseIfNotCurBlock(seekToBlock);
731        // It is important that we compute and pass onDiskSize to the block
732        // reader so that it does not have to read the header separately to
733        // figure out the size. Currently, we do not have a way to do this
734        // correctly in the general case however.
735        // TODO: See https://issues.apache.org/jira/browse/HBASE-14576
736        int prevBlockSize = -1;
737        seekToBlock = reader.readBlock(previousBlockOffset, prevBlockSize, cacheBlocks, pread,
738          isCompaction, true, BlockType.DATA, getEffectiveDataBlockEncoding());
739        // TODO shortcut: seek forward in this block to the last key of the
740        // block.
741      }
742      loadBlockAndSeekToKey(seekToBlock, firstKey, true, key, true);
743      return true;
744    }
745
746    /**
747     * The curBlock will be released by shipping or close method, so only need to consider releasing
748     * the block, which was read from HFile before and not referenced by curBlock.
749     */
750    protected void releaseIfNotCurBlock(HFileBlock block) {
751      if (curBlock != block) {
752        block.release();
753      }
754    }
755
756    /**
757     * Scans blocks in the "scanned" section of the {@link HFile} until the next data block is
758     * found.
759     * @return the next block, or null if there are no more data blocks
760     */
761    @edu.umd.cs.findbugs.annotations.SuppressWarnings(value = "NP_NULL_ON_SOME_PATH",
762        justification = "Yeah, unnecessary null check; could do w/ clean up")
763    protected HFileBlock readNextDataBlock() throws IOException {
764      long lastDataBlockOffset = reader.getTrailer().getLastDataBlockOffset();
765      if (curBlock == null) {
766        return null;
767      }
768      HFileBlock block = this.curBlock;
769      do {
770        if (block.getOffset() >= lastDataBlockOffset) {
771          releaseIfNotCurBlock(block);
772          return null;
773        }
774        if (block.getOffset() < 0) {
775          releaseIfNotCurBlock(block);
776          throw new IOException("Invalid block offset: " + block + ", path=" + reader.getPath());
777        }
778        // We are reading the next block without block type validation, because
779        // it might turn out to be a non-data block.
780        block = reader.readBlock(block.getOffset() + block.getOnDiskSizeWithHeader(),
781          block.getNextBlockOnDiskSize(), cacheBlocks, pread, isCompaction, true, null,
782          getEffectiveDataBlockEncoding());
783        if (block != null && !block.getBlockType().isData()) {
784          // Whatever block we read we will be returning it unless
785          // it is a datablock. Just in case the blocks are non data blocks
786          block.release();
787        }
788      } while (!block.getBlockType().isData());
789      return block;
790    }
791
792    public DataBlockEncoding getEffectiveDataBlockEncoding() {
793      return this.reader.getEffectiveEncodingInCache(isCompaction);
794    }
795
796    @Override
797    public ExtendedCell getCell() {
798      if (!isSeeked()) {
799        return null;
800      }
801
802      ExtendedCell ret;
803      int cellBufSize = getKVBufSize();
804      long seqId = 0L;
805      if (this.reader.getHFileInfo().shouldIncludeMemStoreTS()) {
806        seqId = currMemstoreTS;
807      }
808      if (blockBuffer.hasArray()) {
809        // TODO : reduce the varieties of KV here. Check if based on a boolean
810        // we can handle the 'no tags' case.
811        if (currTagsLen > 0) {
812          ret = new SizeCachedKeyValue(blockBuffer.array(),
813            blockBuffer.arrayOffset() + blockBuffer.position(), cellBufSize, seqId, currKeyLen,
814            rowLen);
815        } else {
816          ret = new SizeCachedNoTagsKeyValue(blockBuffer.array(),
817            blockBuffer.arrayOffset() + blockBuffer.position(), cellBufSize, seqId, currKeyLen,
818            rowLen);
819        }
820      } else {
821        ByteBuffer buf = blockBuffer.asSubByteBuffer(cellBufSize);
822        if (buf.isDirect()) {
823          ret = currTagsLen > 0
824            ? new SizeCachedByteBufferKeyValue(buf, buf.position(), cellBufSize, seqId, currKeyLen,
825              rowLen)
826            : new SizeCachedNoTagsByteBufferKeyValue(buf, buf.position(), cellBufSize, seqId,
827              currKeyLen, rowLen);
828        } else {
829          if (currTagsLen > 0) {
830            ret = new SizeCachedKeyValue(buf.array(), buf.arrayOffset() + buf.position(),
831              cellBufSize, seqId, currKeyLen, rowLen);
832          } else {
833            ret = new SizeCachedNoTagsKeyValue(buf.array(), buf.arrayOffset() + buf.position(),
834              cellBufSize, seqId, currKeyLen, rowLen);
835          }
836        }
837      }
838      return ret;
839    }
840
841    @Override
842    public ExtendedCell getKey() {
843      assertSeeked();
844      // Create a new object so that this getKey is cached as firstKey, lastKey
845      ObjectIntPair<ByteBuffer> keyPair = new ObjectIntPair<>();
846      blockBuffer.asSubByteBuffer(blockBuffer.position() + KEY_VALUE_LEN_SIZE, currKeyLen, keyPair);
847      ByteBuffer keyBuf = keyPair.getFirst();
848      if (keyBuf.hasArray()) {
849        return new KeyValue.KeyOnlyKeyValue(keyBuf.array(),
850          keyBuf.arrayOffset() + keyPair.getSecond(), currKeyLen);
851      } else {
852        // Better to do a copy here instead of holding on to this BB so that
853        // we could release the blocks referring to this key. This key is specifically used
854        // in HalfStoreFileReader to get the firstkey and lastkey by creating a new scanner
855        // every time. So holding onto the BB (incase of DBB) is not advised here.
856        byte[] key = new byte[currKeyLen];
857        ByteBufferUtils.copyFromBufferToArray(key, keyBuf, keyPair.getSecond(), 0, currKeyLen);
858        return new KeyValue.KeyOnlyKeyValue(key, 0, currKeyLen);
859      }
860    }
861
862    @Override
863    public ByteBuffer getValue() {
864      assertSeeked();
865      // Okie to create new Pair. Not used in hot path
866      ObjectIntPair<ByteBuffer> valuePair = new ObjectIntPair<>();
867      this.blockBuffer.asSubByteBuffer(blockBuffer.position() + KEY_VALUE_LEN_SIZE + currKeyLen,
868        currValueLen, valuePair);
869      ByteBuffer valBuf = valuePair.getFirst().duplicate();
870      valBuf.position(valuePair.getSecond());
871      valBuf.limit(currValueLen + valuePair.getSecond());
872      return valBuf.slice();
873    }
874
875    protected void setNonSeekedState() {
876      reset();
877      blockBuffer = null;
878      currKeyLen = 0;
879      currValueLen = 0;
880      currMemstoreTS = 0;
881      currMemstoreTSLen = 0;
882      currTagsLen = 0;
883    }
884
885    /**
886     * Set the position on current backing blockBuffer.
887     */
888    private void positionThisBlockBuffer() {
889      try {
890        blockBuffer.skip(getCurCellSerializedSize());
891      } catch (IllegalArgumentException e) {
892        LOG.error("Current pos = " + blockBuffer.position() + "; currKeyLen = " + currKeyLen
893          + "; currValLen = " + currValueLen + "; block limit = " + blockBuffer.limit()
894          + "; currBlock currBlockOffset = " + this.curBlock.getOffset() + "; path="
895          + reader.getPath());
896        throw e;
897      }
898    }
899
900    /**
901     * Set our selves up for the next 'next' invocation, set up next block.
902     * @return True is more to read else false if at the end.
903     */
904    private boolean positionForNextBlock() throws IOException {
905      // Methods are small so they get inlined because they are 'hot'.
906      long lastDataBlockOffset = reader.getTrailer().getLastDataBlockOffset();
907      if (this.curBlock.getOffset() >= lastDataBlockOffset) {
908        setNonSeekedState();
909        return false;
910      }
911      return isNextBlock();
912    }
913
914    private boolean isNextBlock() throws IOException {
915      // Methods are small so they get inlined because they are 'hot'.
916      HFileBlock nextBlock = readNextDataBlock();
917      if (nextBlock == null) {
918        setNonSeekedState();
919        return false;
920      }
921      updateCurrentBlock(nextBlock);
922      return true;
923    }
924
925    private final boolean _next() throws IOException {
926      // Small method so can be inlined. It is a hot one.
927      if (blockBuffer.remaining() <= 0) {
928        return positionForNextBlock();
929      }
930
931      // We are still in the same block.
932      readKeyValueLen();
933      return true;
934    }
935
936    /**
937     * Go to the next key/value in the block section. Loads the next block if necessary. If
938     * successful, {@link #getKey()} and {@link #getValue()} can be called.
939     * @return true if successfully navigated to the next key/value
940     */
941    @Override
942    public boolean next() throws IOException {
943      // This is a hot method so extreme measures taken to ensure it is small and inlineable.
944      // Checked by setting: -XX:+UnlockDiagnosticVMOptions -XX:+PrintInlining -XX:+PrintCompilation
945      assertSeeked();
946      positionThisBlockBuffer();
947      return _next();
948    }
949
950    /**
951     * Positions this scanner at the start of the file.
952     * @return false if empty file; i.e. a call to next would return false and the current key and
953     *         value are undefined.
954     */
955    @Override
956    public boolean seekTo() throws IOException {
957      if (reader == null) {
958        return false;
959      }
960
961      if (reader.getTrailer().getEntryCount() == 0) {
962        // No data blocks.
963        return false;
964      }
965
966      long firstDataBlockOffset = reader.getTrailer().getFirstDataBlockOffset();
967      if (curBlock != null && curBlock.getOffset() == firstDataBlockOffset) {
968        return processFirstDataBlock();
969      }
970
971      readAndUpdateNewBlock(firstDataBlockOffset);
972      return true;
973    }
974
975    protected boolean processFirstDataBlock() throws IOException {
976      blockBuffer.rewind();
977      readKeyValueLen();
978      return true;
979    }
980
981    protected void readAndUpdateNewBlock(long firstDataBlockOffset) throws IOException {
982      HFileBlock newBlock = reader.readBlock(firstDataBlockOffset, -1, cacheBlocks, pread,
983        isCompaction, true, BlockType.DATA, getEffectiveDataBlockEncoding());
984      if (newBlock.getOffset() < 0) {
985        releaseIfNotCurBlock(newBlock);
986        throw new IOException(
987          "Invalid offset=" + newBlock.getOffset() + ", path=" + reader.getPath());
988      }
989      updateCurrentBlock(newBlock);
990    }
991
992    protected int loadBlockAndSeekToKey(HFileBlock seekToBlock, ExtendedCell nextIndexedKey,
993      boolean rewind, ExtendedCell key, boolean seekBefore) throws IOException {
994      if (this.curBlock == null || this.curBlock.getOffset() != seekToBlock.getOffset()) {
995        updateCurrentBlock(seekToBlock);
996      } else if (rewind) {
997        blockBuffer.rewind();
998      }
999      // Update the nextIndexedKey
1000      this.nextIndexedKey = nextIndexedKey;
1001      return blockSeek(key, seekBefore);
1002    }
1003
1004    /** Returns True if v &lt;= 0 or v &gt; current block buffer limit. */
1005    protected final boolean checkKeyLen(final int v) {
1006      return v <= 0 || v > this.blockBuffer.limit();
1007    }
1008
1009    /** Returns True if v &lt; 0 or v &gt; current block buffer limit. */
1010    protected final boolean checkLen(final int v) {
1011      return v < 0 || v > this.blockBuffer.limit();
1012    }
1013
1014    /**
1015     * Check key and value lengths are wholesome.
1016     */
1017    protected final void checkKeyValueLen() {
1018      if (checkKeyLen(this.currKeyLen) || checkLen(this.currValueLen)) {
1019        throw new IllegalStateException("Invalid currKeyLen " + this.currKeyLen
1020          + " or currValueLen " + this.currValueLen + ". Block offset: " + this.curBlock.getOffset()
1021          + ", block length: " + this.blockBuffer.limit() + ", position: "
1022          + this.blockBuffer.position() + " (without header)." + ", path=" + reader.getPath());
1023      }
1024    }
1025
1026    /**
1027     * Updates the current block to be the given {@link HFileBlock}. Seeks to the the first
1028     * key/value pair.
1029     * @param newBlock the block read by {@link HFileReaderImpl#readBlock}, it's a totally new block
1030     *                 with new allocated {@link ByteBuff}, so if no further reference to this
1031     *                 block, we should release it carefully.
1032     */
1033    protected void updateCurrentBlock(HFileBlock newBlock) throws IOException {
1034      try {
1035        if (newBlock.getBlockType() != BlockType.DATA) {
1036          throw new IllegalStateException(
1037            "ScannerV2 works only on data blocks, got " + newBlock.getBlockType() + "; "
1038              + "HFileName=" + reader.getPath() + ", " + "dataBlockEncoder="
1039              + reader.getDataBlockEncoding() + ", " + "isCompaction=" + isCompaction);
1040        }
1041        updateCurrBlockRef(newBlock);
1042        blockBuffer = newBlock.getBufferWithoutHeader();
1043        readKeyValueLen();
1044      } finally {
1045        releaseIfNotCurBlock(newBlock);
1046      }
1047      // Reset the next indexed key
1048      this.nextIndexedKey = null;
1049    }
1050
1051    protected ExtendedCell getFirstKeyCellInBlock(HFileBlock curBlock) {
1052      ByteBuff buffer = curBlock.getBufferWithoutHeader();
1053      // It is safe to manipulate this buffer because we own the buffer object.
1054      buffer.rewind();
1055      int klen = buffer.getInt();
1056      buffer.skip(Bytes.SIZEOF_INT);// Skip value len part
1057      ByteBuffer keyBuff = buffer.asSubByteBuffer(klen);
1058      if (keyBuff.hasArray()) {
1059        return new KeyValue.KeyOnlyKeyValue(keyBuff.array(),
1060          keyBuff.arrayOffset() + keyBuff.position(), klen);
1061      } else {
1062        return new ByteBufferKeyOnlyKeyValue(keyBuff, keyBuff.position(), klen);
1063      }
1064    }
1065
1066    public int compareKey(CellComparator comparator, ExtendedCell key) {
1067      blockBuffer.asSubByteBuffer(blockBuffer.position() + KEY_VALUE_LEN_SIZE, currKeyLen, pair);
1068      this.bufBackedKeyOnlyKv.setKey(pair.getFirst(), pair.getSecond(), currKeyLen, rowLen);
1069      return PrivateCellUtil.compareKeyIgnoresMvcc(comparator, key, this.bufBackedKeyOnlyKv);
1070    }
1071
1072    @Override
1073    public void shipped() throws IOException {
1074      this.returnBlocks(false);
1075    }
1076  }
1077
1078  @Override
1079  public Path getPath() {
1080    return path;
1081  }
1082
1083  @Override
1084  public DataBlockEncoding getDataBlockEncoding() {
1085    return dataBlockEncoder.getDataBlockEncoding();
1086  }
1087
1088  @Override
1089  public Configuration getConf() {
1090    return conf;
1091  }
1092
1093  @Override
1094  public void setConf(Configuration conf) {
1095    this.conf = conf;
1096  }
1097
1098  /** Minor versions in HFile starting with this number have hbase checksums */
1099  public static final int MINOR_VERSION_WITH_CHECKSUM = 1;
1100  /** In HFile minor version that does not support checksums */
1101  public static final int MINOR_VERSION_NO_CHECKSUM = 0;
1102
1103  /** HFile minor version that introduced pbuf filetrailer */
1104  public static final int PBUF_TRAILER_MINOR_VERSION = 2;
1105
1106  /**
1107   * The size of a (key length, value length) tuple that prefixes each entry in a data block.
1108   */
1109  public final static int KEY_VALUE_LEN_SIZE = 2 * Bytes.SIZEOF_INT;
1110
1111  /**
1112   * Retrieve block from cache. Validates the retrieved block's type vs {@code expectedBlockType}
1113   * and its encoding vs. {@code expectedDataBlockEncoding}. Unpacks the block as necessary.
1114   */
1115  @InterfaceAudience.LimitedPrivate(HBaseInterfaceAudience.UNITTEST)
1116  public HFileBlock getCachedBlock(BlockCacheKey cacheKey, boolean cacheBlock, boolean useLock,
1117    boolean updateCacheMetrics, BlockType expectedBlockType,
1118    DataBlockEncoding expectedDataBlockEncoding) throws IOException {
1119    // Check cache for block. If found return.
1120    BlockCache cache = cacheConf.getBlockCache().orElse(null);
1121    long cachedBlockBytesRead = 0;
1122    if (cache != null) {
1123      HFileBlock cachedBlock = null;
1124      boolean isScanMetricsEnabled = ThreadLocalServerSideScanMetrics.isScanMetricsEnabled();
1125      try {
1126        cachedBlock = (HFileBlock) cache.getBlock(cacheKey, cacheBlock, useLock, updateCacheMetrics,
1127          expectedBlockType);
1128        if (cachedBlock != null) {
1129          if (cacheConf.shouldCacheCompressed(cachedBlock.getBlockType().getCategory())) {
1130            HFileBlock compressedBlock = cachedBlock;
1131            cachedBlock = compressedBlock.unpack(hfileContext, fsBlockReader);
1132            // In case of compressed block after unpacking we can release the compressed block
1133            if (compressedBlock != cachedBlock) {
1134              compressedBlock.release();
1135            }
1136          }
1137          try {
1138            validateBlockType(cachedBlock, expectedBlockType);
1139          } catch (IOException e) {
1140            returnAndEvictBlock(cache, cacheKey, cachedBlock);
1141            cachedBlock = null;
1142            throw e;
1143          }
1144
1145          if (expectedDataBlockEncoding == null) {
1146            return cachedBlock;
1147          }
1148          DataBlockEncoding actualDataBlockEncoding = cachedBlock.getDataBlockEncoding();
1149          // Block types other than data blocks always have
1150          // DataBlockEncoding.NONE. To avoid false negative cache misses, only
1151          // perform this check if cached block is a data block.
1152          if (
1153            cachedBlock.getBlockType().isData()
1154              && !actualDataBlockEncoding.equals(expectedDataBlockEncoding)
1155          ) {
1156            // This mismatch may happen if a Scanner, which is used for say a
1157            // compaction, tries to read an encoded block from the block cache.
1158            // The reverse might happen when an EncodedScanner tries to read
1159            // un-encoded blocks which were cached earlier.
1160            //
1161            // Because returning a data block with an implicit BlockType mismatch
1162            // will cause the requesting scanner to throw a disk read should be
1163            // forced here. This will potentially cause a significant number of
1164            // cache misses, so update so we should keep track of this as it might
1165            // justify the work on a CompoundScanner.
1166            if (
1167              !expectedDataBlockEncoding.equals(DataBlockEncoding.NONE)
1168                && !actualDataBlockEncoding.equals(DataBlockEncoding.NONE)
1169            ) {
1170              // If the block is encoded but the encoding does not match the
1171              // expected encoding it is likely the encoding was changed but the
1172              // block was not yet evicted. Evictions on file close happen async
1173              // so blocks with the old encoding still linger in cache for some
1174              // period of time. This event should be rare as it only happens on
1175              // schema definition change.
1176              LOG.info(
1177                "Evicting cached block with key {} because data block encoding mismatch; "
1178                  + "expected {}, actual {}, path={}",
1179                cacheKey, actualDataBlockEncoding, expectedDataBlockEncoding, path);
1180              // This is an error scenario. so here we need to release the block.
1181              returnAndEvictBlock(cache, cacheKey, cachedBlock);
1182            }
1183            cachedBlock = null;
1184            return null;
1185          }
1186          return cachedBlock;
1187        }
1188      } catch (Exception e) {
1189        if (cachedBlock != null) {
1190          returnAndEvictBlock(cache, cacheKey, cachedBlock);
1191        }
1192        LOG.warn("Failed retrieving block from cache with key {}. "
1193          + "\n Evicting this block from cache and will read it from file system. "
1194          + "\n Exception details: ", cacheKey, e);
1195        if (LOG.isDebugEnabled()) {
1196          LOG.debug("Further tracing details for failed block cache retrieval:"
1197            + "\n Complete File path - {}," + "\n Expected Block Type - {}, Actual Block Type - {},"
1198            + "\n Cache compressed - {}" + "\n Header size (after deserialized from cache) - {}"
1199            + "\n Size with header - {}" + "\n Uncompressed size without header - {} "
1200            + "\n Total byte buffer size - {}" + "\n Encoding code - {}", this.path,
1201            expectedBlockType, (cachedBlock != null ? cachedBlock.getBlockType() : "N/A"),
1202            (expectedBlockType != null
1203              ? cacheConf.shouldCacheCompressed(expectedBlockType.getCategory())
1204              : "N/A"),
1205            (cachedBlock != null ? cachedBlock.headerSize() : "N/A"),
1206            (cachedBlock != null ? cachedBlock.getOnDiskSizeWithHeader() : "N/A"),
1207            (cachedBlock != null ? cachedBlock.getUncompressedSizeWithoutHeader() : "N/A"),
1208            (cachedBlock != null ? cachedBlock.getBufferReadOnly().limit() : "N/A"),
1209            (cachedBlock != null
1210              ? cachedBlock.getBufferReadOnly().getShort(cachedBlock.headerSize())
1211              : "N/A"));
1212        }
1213        return null;
1214      } finally {
1215        // Count bytes read as cached block is being returned
1216        if (isScanMetricsEnabled && cachedBlock != null) {
1217          cachedBlockBytesRead = cachedBlock.getOnDiskSizeWithHeader();
1218          // Account for the header size of the next block if it exists
1219          if (cachedBlock.getNextBlockOnDiskSize() > 0) {
1220            cachedBlockBytesRead += cachedBlock.headerSize();
1221          }
1222        }
1223        if (cachedBlockBytesRead > 0) {
1224          ThreadLocalServerSideScanMetrics.addBytesReadFromBlockCache(cachedBlockBytesRead);
1225        }
1226      }
1227    }
1228    return null;
1229  }
1230
1231  private void returnAndEvictBlock(BlockCache cache, BlockCacheKey cacheKey, Cacheable block) {
1232    block.release();
1233    cache.evictBlock(cacheKey);
1234  }
1235
1236  /**
1237   * @param cacheBlock Add block to cache, if found
1238   * @return block wrapped in a ByteBuffer, with header skipped
1239   */
1240  @Override
1241  public HFileBlock getMetaBlock(String metaBlockName, boolean cacheBlock) throws IOException {
1242    if (trailer.getMetaIndexCount() == 0) {
1243      return null; // there are no meta blocks
1244    }
1245    if (metaBlockIndexReader == null) {
1246      throw new IOException(path + " meta index not loaded");
1247    }
1248
1249    byte[] mbname = Bytes.toBytes(metaBlockName);
1250    int block = metaBlockIndexReader.rootBlockContainingKey(mbname, 0, mbname.length);
1251    if (block == -1) {
1252      return null;
1253    }
1254    long blockSize = metaBlockIndexReader.getRootBlockDataSize(block);
1255
1256    // Per meta key from any given file, synchronize reads for said block. This
1257    // is OK to do for meta blocks because the meta block index is always
1258    // single-level.
1259    synchronized (metaBlockIndexReader.getRootBlockKey(block)) {
1260      // Check cache for block. If found return.
1261      long metaBlockOffset = metaBlockIndexReader.getRootBlockOffset(block);
1262      BlockCacheKey cacheKey = new BlockCacheKey(fileName, family, region, metaBlockOffset,
1263        this.isPrimaryReplicaReader(), BlockType.META, archived);
1264
1265      cacheBlock &=
1266        cacheConf.shouldCacheBlockOnRead(BlockType.META.getCategory(), getHFileInfo(), conf);
1267      HFileBlock cachedBlock =
1268        getCachedBlock(cacheKey, cacheBlock, false, true, BlockType.META, null);
1269      if (cachedBlock != null) {
1270        assert cachedBlock.isUnpacked() : "Packed block leak.";
1271        // Return a distinct 'shallow copy' of the block,
1272        // so pos does not get messed by the scanner
1273        return cachedBlock;
1274      }
1275      // Cache Miss, please load.
1276
1277      HFileBlock compressedBlock =
1278        fsBlockReader.readBlockData(metaBlockOffset, blockSize, true, false, true);
1279      HFileBlock uncompressedBlock = compressedBlock.unpack(hfileContext, fsBlockReader);
1280      if (compressedBlock != uncompressedBlock) {
1281        compressedBlock.release();
1282      }
1283
1284      // Cache the block
1285      if (cacheBlock) {
1286        cacheConf.getBlockCache().ifPresent(
1287          cache -> cache.cacheBlock(cacheKey, uncompressedBlock, cacheConf.isInMemory()));
1288      }
1289      return uncompressedBlock;
1290    }
1291  }
1292
1293  /**
1294   * Whether we use heap or not depends on our intent to cache the block. We want to avoid
1295   * allocating to off-heap if we intend to cache into the on-heap L1 cache. Otherwise, it's more
1296   * efficient to allocate to off-heap since we can control GC ourselves for those. So our decision
1297   * here breaks down as follows: <br>
1298   * If block cache is disabled, don't use heap. If we're not using the CombinedBlockCache, use heap
1299   * unless caching is disabled for the request. Otherwise, only use heap if caching is enabled and
1300   * the expected block type is not DATA (which goes to off-heap L2 in combined cache).
1301   * @see org.apache.hadoop.hbase.io.hfile.HFileBlock.FSReader#readBlockData(long, long, boolean,
1302   *      boolean, boolean)
1303   */
1304  private boolean shouldUseHeap(BlockType expectedBlockType, boolean cacheBlock) {
1305    if (!cacheConf.getBlockCache().isPresent()) {
1306      return false;
1307    }
1308
1309    // we only cache a block if cacheBlock is true and caching-on-read is enabled in CacheConfig
1310    // we can really only check for that if have an expectedBlockType
1311    if (expectedBlockType != null) {
1312      cacheBlock &= cacheConf.shouldCacheBlockOnRead(expectedBlockType.getCategory());
1313    }
1314
1315    if (!cacheConf.isCombinedBlockCache()) {
1316      // Block to cache in LruBlockCache must be an heap one, if caching enabled. So just allocate
1317      // block memory from heap for saving an extra off-heap to heap copying in that case.
1318      return cacheBlock;
1319    }
1320
1321    return cacheBlock && expectedBlockType != null && !expectedBlockType.isData();
1322  }
1323
1324  @Override
1325  public HFileBlock readBlock(long dataBlockOffset, long onDiskBlockSize, final boolean cacheBlock,
1326    boolean pread, final boolean isCompaction, boolean updateCacheMetrics,
1327    BlockType expectedBlockType, DataBlockEncoding expectedDataBlockEncoding) throws IOException {
1328    return readBlock(dataBlockOffset, onDiskBlockSize, cacheBlock, pread, isCompaction,
1329      updateCacheMetrics, expectedBlockType, expectedDataBlockEncoding, false);
1330  }
1331
1332  @Override
1333  public HFileBlock readBlock(long dataBlockOffset, long onDiskBlockSize, final boolean cacheBlock,
1334    boolean pread, final boolean isCompaction, boolean updateCacheMetrics,
1335    BlockType expectedBlockType, DataBlockEncoding expectedDataBlockEncoding, boolean cacheOnly)
1336    throws IOException {
1337    if (dataBlockIndexReader == null) {
1338      throw new IOException(path + " block index not loaded");
1339    }
1340    long trailerOffset = trailer.getLoadOnOpenDataOffset();
1341    if (dataBlockOffset < 0 || dataBlockOffset >= trailerOffset) {
1342      throw new IOException("Requested block is out of range: " + dataBlockOffset
1343        + ", lastDataBlockOffset: " + trailer.getLastDataBlockOffset()
1344        + ", trailer.getLoadOnOpenDataOffset: " + trailerOffset + ", path=" + path);
1345    }
1346    // For any given block from any given file, synchronize reads for said
1347    // block.
1348    // Without a cache, this synchronizing is needless overhead, but really
1349    // the other choice is to duplicate work (which the cache would prevent you
1350    // from doing).
1351
1352    BlockCacheKey cacheKey = new BlockCacheKey(fileName, family, region, dataBlockOffset,
1353      this.isPrimaryReplicaReader(), expectedBlockType, archived);
1354    boolean useLock = false;
1355    IdLock.Entry lockEntry = null;
1356    final Span span = Span.current();
1357    // BlockCacheKey#toString() is quite expensive to call, so if tracing isn't enabled, don't
1358    // record
1359    Attributes attributes = span.isRecording()
1360      ? Attributes.of(BLOCK_CACHE_KEY_KEY, cacheKey.toString())
1361      : Attributes.empty();
1362    try {
1363      while (true) {
1364        // Check cache for block. If found return.
1365        if (cacheConf.shouldReadBlockFromCache(expectedBlockType) && !cacheOnly) {
1366          if (useLock) {
1367            lockEntry = offsetLock.getLockEntry(dataBlockOffset);
1368          }
1369          // Try and get the block from the block cache. If the useLock variable is true then this
1370          // is the second time through the loop and it should not be counted as a block cache miss.
1371          HFileBlock cachedBlock = getCachedBlock(cacheKey, cacheBlock, useLock, updateCacheMetrics,
1372            expectedBlockType, expectedDataBlockEncoding);
1373          if (cachedBlock != null) {
1374            if (LOG.isTraceEnabled()) {
1375              LOG.trace("Block for file {} is coming from Cache {}",
1376                Bytes.toString(cachedBlock.getHFileContext().getTableName()), cachedBlock);
1377            }
1378            span.addEvent("block cache hit", attributes);
1379            assert cachedBlock.isUnpacked() : "Packed block leak.";
1380            if (cachedBlock.getBlockType().isData()) {
1381              if (updateCacheMetrics) {
1382                HFile.DATABLOCK_READ_COUNT.increment();
1383              }
1384              // Validate encoding type for data blocks. We include encoding
1385              // type in the cache key, and we expect it to match on a cache hit.
1386              if (cachedBlock.getDataBlockEncoding() != dataBlockEncoder.getDataBlockEncoding()) {
1387                // Remember to release the block when in exceptional path.
1388                cacheConf.getBlockCache().ifPresent(cache -> {
1389                  returnAndEvictBlock(cache, cacheKey, cachedBlock);
1390                });
1391                throw new IOException("Cached block under key " + cacheKey + " "
1392                  + "has wrong encoding: " + cachedBlock.getDataBlockEncoding() + " (expected: "
1393                  + dataBlockEncoder.getDataBlockEncoding() + "), path=" + path);
1394              }
1395            }
1396            // Cache-hit. Return!
1397            return cachedBlock;
1398          }
1399
1400          if (!useLock && cacheBlock && cacheConf.shouldLockOnCacheMiss(expectedBlockType)) {
1401            // check cache again with lock
1402            useLock = true;
1403            continue;
1404          }
1405          // Carry on, please load.
1406        }
1407
1408        span.addEvent("block cache miss", attributes);
1409        // Load block from filesystem.
1410        HFileBlock hfileBlock = fsBlockReader.readBlockData(dataBlockOffset, onDiskBlockSize, pread,
1411          !isCompaction, shouldUseHeap(expectedBlockType, cacheBlock));
1412        try {
1413          validateBlockType(hfileBlock, expectedBlockType);
1414        } catch (IOException e) {
1415          hfileBlock.release();
1416          throw e;
1417        }
1418        BlockType.BlockCategory category = hfileBlock.getBlockType().getCategory();
1419        final boolean cacheCompressed = cacheConf.shouldCacheCompressed(category);
1420        final boolean cacheOnRead =
1421          cacheConf.shouldCacheBlockOnRead(category, getHFileInfo(), conf);
1422
1423        // Don't need the unpacked block back and we're storing the block in the cache compressed
1424        if (cacheOnly && cacheCompressed && cacheOnRead) {
1425          HFileBlock blockNoChecksum = BlockCacheUtil.getBlockForCaching(cacheConf, hfileBlock);
1426          cacheConf.getBlockCache().ifPresent(cache -> {
1427            LOG.debug("Skipping decompression of block {} in prefetch", cacheKey);
1428            // Cache the block if necessary
1429            if (cacheBlock && cacheOnRead) {
1430              cache.cacheBlock(cacheKey, blockNoChecksum, cacheConf.isInMemory(), cacheOnly);
1431            }
1432          });
1433
1434          if (updateCacheMetrics && hfileBlock.getBlockType().isData()) {
1435            HFile.DATABLOCK_READ_COUNT.increment();
1436          }
1437          return blockNoChecksum;
1438        }
1439        HFileBlock unpacked = hfileBlock.unpack(hfileContext, fsBlockReader);
1440        HFileBlock unpackedNoChecksum = BlockCacheUtil.getBlockForCaching(cacheConf, unpacked);
1441        // Cache the block if necessary
1442        cacheConf.getBlockCache().ifPresent(cache -> {
1443          if (cacheBlock && cacheOnRead) {
1444            // Using the wait on cache during compaction and prefetching.
1445            cache.cacheBlock(cacheKey,
1446              cacheCompressed
1447                ? BlockCacheUtil.getBlockForCaching(cacheConf, hfileBlock)
1448                : unpackedNoChecksum,
1449              cacheConf.isInMemory(), cacheOnly);
1450          }
1451        });
1452        if (unpacked != hfileBlock) {
1453          // End of life here if hfileBlock is an independent block.
1454          hfileBlock.release();
1455        }
1456        if (updateCacheMetrics && hfileBlock.getBlockType().isData()) {
1457          HFile.DATABLOCK_READ_COUNT.increment();
1458        }
1459
1460        return unpackedNoChecksum;
1461      }
1462    } finally {
1463      if (lockEntry != null) {
1464        offsetLock.releaseLockEntry(lockEntry);
1465      }
1466    }
1467  }
1468
1469  @Override
1470  public boolean hasMVCCInfo() {
1471    return fileInfo.shouldIncludeMemStoreTS() && fileInfo.isDecodeMemstoreTS();
1472  }
1473
1474  /**
1475   * Compares the actual type of a block retrieved from cache or disk with its expected type and
1476   * throws an exception in case of a mismatch. Expected block type of {@link BlockType#DATA} is
1477   * considered to match the actual block type [@link {@link BlockType#ENCODED_DATA} as well.
1478   * @param block             a block retrieved from cache or disk
1479   * @param expectedBlockType the expected block type, or null to skip the check
1480   */
1481  private void validateBlockType(HFileBlock block, BlockType expectedBlockType) throws IOException {
1482    if (expectedBlockType == null) {
1483      return;
1484    }
1485    BlockType actualBlockType = block.getBlockType();
1486    if (expectedBlockType.isData() && actualBlockType.isData()) {
1487      // We consider DATA to match ENCODED_DATA for the purpose of this
1488      // verification.
1489      return;
1490    }
1491    if (actualBlockType != expectedBlockType) {
1492      throw new IOException("Expected block type " + expectedBlockType + ", " + "but got "
1493        + actualBlockType + ": " + block + ", path=" + path);
1494    }
1495  }
1496
1497  /**
1498   * @return Last key as cell in the file. May be null if file has no entries. Note that this is not
1499   *         the last row key, but it is the Cell representation of the last key
1500   */
1501  @Override
1502  public Optional<ExtendedCell> getLastKey() {
1503    return dataBlockIndexReader.isEmpty()
1504      ? Optional.empty()
1505      : Optional.of(fileInfo.getLastKeyCell());
1506  }
1507
1508  /**
1509   * @return Midkey for this file. We work with block boundaries only so returned midkey is an
1510   *         approximation only.
1511   */
1512  @Override
1513  public Optional<ExtendedCell> midKey() throws IOException {
1514    return Optional.ofNullable(dataBlockIndexReader.midkey(this));
1515  }
1516
1517  @Override
1518  public void close() throws IOException {
1519    close(cacheConf.shouldEvictOnClose());
1520  }
1521
1522  @Override
1523  public DataBlockEncoding getEffectiveEncodingInCache(boolean isCompaction) {
1524    return dataBlockEncoder.getEffectiveEncodingInCache(isCompaction);
1525  }
1526
1527  /** For testing */
1528  @Override
1529  public HFileBlock.FSReader getUncachedBlockReader() {
1530    return fsBlockReader;
1531  }
1532
1533  /**
1534   * Scanner that operates on encoded data blocks.
1535   */
1536  protected static class EncodedScanner extends HFileScannerImpl {
1537    private final HFileBlockDecodingContext decodingCtx;
1538    private final DataBlockEncoder.EncodedSeeker seeker;
1539    private final DataBlockEncoder dataBlockEncoder;
1540
1541    public EncodedScanner(HFile.Reader reader, boolean cacheBlocks, boolean pread,
1542      boolean isCompaction, HFileContext meta, Configuration conf) {
1543      super(reader, cacheBlocks, pread, isCompaction);
1544      DataBlockEncoding encoding = reader.getDataBlockEncoding();
1545      dataBlockEncoder = encoding.getEncoder();
1546      decodingCtx = dataBlockEncoder.newDataBlockDecodingContext(conf, meta);
1547      seeker = dataBlockEncoder.createSeeker(decodingCtx);
1548    }
1549
1550    @Override
1551    public boolean isSeeked() {
1552      return curBlock != null;
1553    }
1554
1555    @Override
1556    public void setNonSeekedState() {
1557      reset();
1558    }
1559
1560    /**
1561     * Updates the current block to be the given {@link HFileBlock}. Seeks to the the first
1562     * key/value pair.
1563     * @param newBlock the block to make current, and read by {@link HFileReaderImpl#readBlock},
1564     *                 it's a totally new block with new allocated {@link ByteBuff}, so if no
1565     *                 further reference to this block, we should release it carefully.
1566     */
1567    @Override
1568    protected void updateCurrentBlock(HFileBlock newBlock) throws CorruptHFileException {
1569      try {
1570        // sanity checks
1571        if (newBlock.getBlockType() != BlockType.ENCODED_DATA) {
1572          throw new IllegalStateException("EncodedScanner works only on encoded data blocks");
1573        }
1574        short dataBlockEncoderId = newBlock.getDataBlockEncodingId();
1575        if (!DataBlockEncoding.isCorrectEncoder(dataBlockEncoder, dataBlockEncoderId)) {
1576          String encoderCls = dataBlockEncoder.getClass().getName();
1577          throw new CorruptHFileException(
1578            "Encoder " + encoderCls + " doesn't support data block encoding "
1579              + DataBlockEncoding.getNameFromId(dataBlockEncoderId) + ",path=" + reader.getPath());
1580        }
1581        updateCurrBlockRef(newBlock);
1582        ByteBuff encodedBuffer = getEncodedBuffer(newBlock);
1583        seeker.setCurrentBuffer(encodedBuffer);
1584      } finally {
1585        releaseIfNotCurBlock(newBlock);
1586      }
1587      // Reset the next indexed key
1588      this.nextIndexedKey = null;
1589    }
1590
1591    private ByteBuff getEncodedBuffer(HFileBlock newBlock) {
1592      ByteBuff origBlock = newBlock.getBufferReadOnly();
1593      int pos = newBlock.headerSize() + DataBlockEncoding.ID_SIZE;
1594      origBlock.position(pos);
1595      origBlock
1596        .limit(pos + newBlock.getUncompressedSizeWithoutHeader() - DataBlockEncoding.ID_SIZE);
1597      return origBlock.slice();
1598    }
1599
1600    @Override
1601    protected boolean processFirstDataBlock() throws IOException {
1602      seeker.rewind();
1603      return true;
1604    }
1605
1606    @Override
1607    public boolean next() throws IOException {
1608      boolean isValid = seeker.next();
1609      if (!isValid) {
1610        HFileBlock newBlock = readNextDataBlock();
1611        isValid = newBlock != null;
1612        if (isValid) {
1613          updateCurrentBlock(newBlock);
1614        } else {
1615          setNonSeekedState();
1616        }
1617      }
1618      return isValid;
1619    }
1620
1621    @Override
1622    public ExtendedCell getKey() {
1623      assertValidSeek();
1624      return seeker.getKey();
1625    }
1626
1627    @Override
1628    public ByteBuffer getValue() {
1629      assertValidSeek();
1630      return seeker.getValueShallowCopy();
1631    }
1632
1633    @Override
1634    public ExtendedCell getCell() {
1635      if (this.curBlock == null) {
1636        return null;
1637      }
1638      return seeker.getCell();
1639    }
1640
1641    private void assertValidSeek() {
1642      if (this.curBlock == null) {
1643        throw new NotSeekedException(reader.getPath());
1644      }
1645    }
1646
1647    @Override
1648    protected ExtendedCell getFirstKeyCellInBlock(HFileBlock curBlock) {
1649      return dataBlockEncoder.getFirstKeyCellInBlock(getEncodedBuffer(curBlock));
1650    }
1651
1652    @Override
1653    protected int loadBlockAndSeekToKey(HFileBlock seekToBlock, ExtendedCell nextIndexedKey,
1654      boolean rewind, ExtendedCell key, boolean seekBefore) throws IOException {
1655      if (this.curBlock == null || this.curBlock.getOffset() != seekToBlock.getOffset()) {
1656        updateCurrentBlock(seekToBlock);
1657      } else if (rewind) {
1658        seeker.rewind();
1659      }
1660      this.nextIndexedKey = nextIndexedKey;
1661      return seeker.seekToKeyInBlock(key, seekBefore);
1662    }
1663
1664    @Override
1665    public int compareKey(CellComparator comparator, ExtendedCell key) {
1666      return seeker.compareKey(comparator, key);
1667    }
1668  }
1669
1670  /**
1671   * Returns a buffer with the Bloom filter metadata. The caller takes ownership of the buffer.
1672   */
1673  @Override
1674  public DataInput getGeneralBloomFilterMetadata() throws IOException {
1675    return this.getBloomFilterMetadata(BlockType.GENERAL_BLOOM_META);
1676  }
1677
1678  @Override
1679  public DataInput getDeleteBloomFilterMetadata() throws IOException {
1680    return this.getBloomFilterMetadata(BlockType.DELETE_FAMILY_BLOOM_META);
1681  }
1682
1683  private DataInput getBloomFilterMetadata(BlockType blockType) throws IOException {
1684    if (
1685      blockType != BlockType.GENERAL_BLOOM_META && blockType != BlockType.DELETE_FAMILY_BLOOM_META
1686    ) {
1687      throw new RuntimeException(
1688        "Block Type: " + blockType.toString() + " is not supported, path=" + path);
1689    }
1690
1691    for (HFileBlock b : fileInfo.getLoadOnOpenBlocks()) {
1692      if (b.getBlockType() == blockType) {
1693        return b.getByteStream();
1694      }
1695    }
1696    return null;
1697  }
1698
1699  public boolean isFileInfoLoaded() {
1700    return true; // We load file info in constructor in version 2.
1701  }
1702
1703  @Override
1704  public HFileContext getFileContext() {
1705    return hfileContext;
1706  }
1707
1708  /**
1709   * Returns false if block prefetching was requested for this file and has not completed, true
1710   * otherwise
1711   */
1712  @Override
1713  public boolean prefetchComplete() {
1714    return PrefetchExecutor.isCompleted(path);
1715  }
1716
1717  /**
1718   * Returns true if block prefetching was started after waiting for specified delay, false
1719   * otherwise
1720   */
1721  @Override
1722  public boolean prefetchStarted() {
1723    return PrefetchExecutor.isPrefetchStarted();
1724  }
1725
1726  /**
1727   * Create a Scanner on this file. No seeks or reads are done on creation. Call
1728   * {@link HFileScanner#seekTo(ExtendedCell)} to position an start the read. There is nothing to
1729   * clean up in a Scanner. Letting go of your references to the scanner is sufficient. NOTE: Do not
1730   * use this overload of getScanner for compactions. See
1731   * {@link #getScanner(Configuration, boolean, boolean, boolean)}
1732   * @param conf        Store configuration.
1733   * @param cacheBlocks True if we should cache blocks read in by this scanner.
1734   * @param pread       Use positional read rather than seek+read if true (pread is better for
1735   *                    random reads, seek+read is better scanning).
1736   * @return Scanner on this file.
1737   */
1738  @Override
1739  public HFileScanner getScanner(Configuration conf, boolean cacheBlocks, final boolean pread) {
1740    return getScanner(conf, cacheBlocks, pread, false);
1741  }
1742
1743  /**
1744   * Create a Scanner on this file. No seeks or reads are done on creation. Call
1745   * {@link HFileScanner#seekTo(ExtendedCell)} to position an start the read. There is nothing to
1746   * clean up in a Scanner. Letting go of your references to the scanner is sufficient.
1747   * @param conf         Store configuration.
1748   * @param cacheBlocks  True if we should cache blocks read in by this scanner.
1749   * @param pread        Use positional read rather than seek+read if true (pread is better for
1750   *                     random reads, seek+read is better scanning).
1751   * @param isCompaction is scanner being used for a compaction?
1752   * @return Scanner on this file.
1753   */
1754  @Override
1755  public HFileScanner getScanner(Configuration conf, boolean cacheBlocks, final boolean pread,
1756    final boolean isCompaction) {
1757    if (dataBlockEncoder.useEncodedScanner()) {
1758      return new EncodedScanner(this, cacheBlocks, pread, isCompaction, this.hfileContext, conf);
1759    }
1760    return new HFileScannerImpl(this, cacheBlocks, pread, isCompaction);
1761  }
1762
1763  public int getMajorVersion() {
1764    return 3;
1765  }
1766
1767  @Override
1768  public void unbufferStream() {
1769    fsBlockReader.unbufferStream();
1770  }
1771}