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 <= 0 or v > 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 < 0 or v > 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}