View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  package org.apache.hadoop.hbase.io.hfile;
19  
20  import java.io.DataInput;
21  import java.io.IOException;
22  import java.nio.ByteBuffer;
23  import java.security.Key;
24  import java.security.KeyException;
25  import java.util.ArrayList;
26  import java.util.List;
27  
28  import org.apache.commons.logging.Log;
29  import org.apache.commons.logging.LogFactory;
30  import org.apache.hadoop.hbase.classification.InterfaceAudience;
31  import org.apache.hadoop.conf.Configurable;
32  import org.apache.hadoop.conf.Configuration;
33  import org.apache.hadoop.fs.Path;
34  import org.apache.hadoop.hbase.Cell;
35  import org.apache.hadoop.hbase.CellComparator;
36  import org.apache.hadoop.hbase.CellUtil;
37  import org.apache.hadoop.hbase.ByteBufferedKeyOnlyKeyValue;
38  import org.apache.hadoop.hbase.OffheapKeyValue;
39  import org.apache.hadoop.hbase.ShareableMemory;
40  import org.apache.hadoop.hbase.SizeCachedKeyValue;
41  import org.apache.hadoop.hbase.HConstants;
42  import org.apache.hadoop.hbase.KeyValue;
43  import org.apache.hadoop.hbase.SizeCachedNoTagsKeyValue;
44  import org.apache.hadoop.hbase.fs.HFileSystem;
45  import org.apache.hadoop.hbase.io.FSDataInputStreamWrapper;
46  import org.apache.hadoop.hbase.io.compress.Compression;
47  import org.apache.hadoop.hbase.io.crypto.Cipher;
48  import org.apache.hadoop.hbase.io.crypto.Encryption;
49  import org.apache.hadoop.hbase.io.encoding.DataBlockEncoder;
50  import org.apache.hadoop.hbase.io.encoding.DataBlockEncoding;
51  import org.apache.hadoop.hbase.io.encoding.HFileBlockDecodingContext;
52  import org.apache.hadoop.hbase.io.hfile.HFile.FileInfo;
53  import org.apache.hadoop.hbase.nio.ByteBuff;
54  import org.apache.hadoop.hbase.security.EncryptionUtil;
55  import org.apache.hadoop.hbase.security.User;
56  import org.apache.hadoop.hbase.util.ByteBufferUtils;
57  import org.apache.hadoop.hbase.util.Bytes;
58  import org.apache.hadoop.hbase.util.IdLock;
59  import org.apache.hadoop.hbase.util.ObjectIntPair;
60  import org.apache.hadoop.io.WritableUtils;
61  import org.apache.htrace.Trace;
62  import org.apache.htrace.TraceScope;
63  
64  import com.google.common.annotations.VisibleForTesting;
65  
66  /**
67   * Implementation that can handle all hfile versions of {@link HFile.Reader}.
68   */
69  @InterfaceAudience.Private
70  @edu.umd.cs.findbugs.annotations.SuppressWarnings(value="URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
71  public class HFileReaderImpl implements HFile.Reader, Configurable {
72    // This class is HFileReaderV3 + HFileReaderV2 + AbstractHFileReader all squashed together into
73    // one file.  Ditto for all the HFileReader.ScannerV? implementations. I was running up against
74    // the MaxInlineLevel limit because too many tiers involved reading from an hfile. Was also hard
75    // to navigate the source code when so many classes participating in read.
76    private static final Log LOG = LogFactory.getLog(HFileReaderImpl.class);
77  
78    /** Data block index reader keeping the root data index in memory */
79    private HFileBlockIndex.CellBasedKeyBlockIndexReader dataBlockIndexReader;
80  
81    /** Meta block index reader -- always single level */
82    private HFileBlockIndex.ByteArrayKeyBlockIndexReader metaBlockIndexReader;
83  
84    private final FixedFileTrailer trailer;
85  
86    /** Filled when we read in the trailer. */
87    private final Compression.Algorithm compressAlgo;
88  
89    private boolean isPrimaryReplicaReader;
90  
91    /**
92     * What kind of data block encoding should be used while reading, writing,
93     * and handling cache.
94     */
95    private HFileDataBlockEncoder dataBlockEncoder = NoOpDataBlockEncoder.INSTANCE;
96  
97    /** Last key in the file. Filled in when we read in the file info */
98    private Cell lastKeyCell = null;
99  
100   /** Average key length read from file info */
101   private int avgKeyLen = -1;
102 
103   /** Average value length read from file info */
104   private int avgValueLen = -1;
105 
106   /** Key comparator */
107   private CellComparator comparator = CellComparator.COMPARATOR;
108 
109   /** Size of this file. */
110   private final long fileSize;
111 
112   /** Block cache configuration. */
113   private final CacheConfig cacheConf;
114 
115   /** Path of file */
116   private final Path path;
117 
118   /** File name to be used for block names */
119   private final String name;
120 
121   private FileInfo fileInfo;
122 
123   private Configuration conf;
124 
125   private HFileContext hfileContext;
126 
127   /** Filesystem-level block reader. */
128   private HFileBlock.FSReader fsBlockReader;
129 
130   /**
131    * A "sparse lock" implementation allowing to lock on a particular block
132    * identified by offset. The purpose of this is to avoid two clients loading
133    * the same block, and have all but one client wait to get the block from the
134    * cache.
135    */
136   private IdLock offsetLock = new IdLock();
137 
138   /**
139    * Blocks read from the load-on-open section, excluding data root index, meta
140    * index, and file info.
141    */
142   private List<HFileBlock> loadOnOpenBlocks = new ArrayList<HFileBlock>();
143 
144   /** Minimum minor version supported by this HFile format */
145   static final int MIN_MINOR_VERSION = 0;
146 
147   /** Maximum minor version supported by this HFile format */
148   // We went to version 2 when we moved to pb'ing fileinfo and the trailer on
149   // the file. This version can read Writables version 1.
150   static final int MAX_MINOR_VERSION = 3;
151 
152   /**
153    * We can read files whose major version is v2 IFF their minor version is at least 3.
154    */
155   private static final int MIN_V2_MINOR_VERSION_WITH_PB = 3;
156 
157   /** Minor versions starting with this number have faked index key */
158   static final int MINOR_VERSION_WITH_FAKED_KEY = 3;
159 
160   /**
161    * Opens a HFile. You must load the index before you can use it by calling
162    * {@link #loadFileInfo()}.
163    * @param path
164    *          Path to HFile.
165    * @param trailer
166    *          File trailer.
167    * @param fsdis
168    *          input stream.
169    * @param fileSize
170    *          Length of the stream.
171    * @param cacheConf
172    *          Cache configuration.
173    * @param hfs
174    *          The file system.
175    * @param conf
176    *          Configuration
177    */
178   @edu.umd.cs.findbugs.annotations.SuppressWarnings(value="URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
179   public HFileReaderImpl(final Path path, FixedFileTrailer trailer,
180       final FSDataInputStreamWrapper fsdis,
181       final long fileSize, final CacheConfig cacheConf, final HFileSystem hfs,
182       final Configuration conf)
183   throws IOException {
184     this.trailer = trailer;
185     this.compressAlgo = trailer.getCompressionCodec();
186     this.cacheConf = cacheConf;
187     this.fileSize = fileSize;
188     this.path = path;
189     this.name = path.getName();
190     this.conf = conf;
191     checkFileVersion();
192     this.hfileContext = createHFileContext(fsdis, fileSize, hfs, path, trailer);
193     this.fsBlockReader = new HFileBlock.FSReaderImpl(fsdis, fileSize, hfs, path, hfileContext);
194 
195     // Comparator class name is stored in the trailer in version 2.
196     comparator = trailer.createComparator();
197     dataBlockIndexReader = new HFileBlockIndex.CellBasedKeyBlockIndexReader(comparator,
198         trailer.getNumDataIndexLevels(), this);
199     metaBlockIndexReader = new HFileBlockIndex.ByteArrayKeyBlockIndexReader(1);
200 
201     // Parse load-on-open data.
202 
203     HFileBlock.BlockIterator blockIter = fsBlockReader.blockRange(
204         trailer.getLoadOnOpenDataOffset(),
205         fileSize - trailer.getTrailerSize());
206 
207     // Data index. We also read statistics about the block index written after
208     // the root level.
209     dataBlockIndexReader.readMultiLevelIndexRoot(
210         blockIter.nextBlockWithBlockType(BlockType.ROOT_INDEX),
211         trailer.getDataIndexCount());
212 
213     // Meta index.
214     metaBlockIndexReader.readRootIndex(
215         blockIter.nextBlockWithBlockType(BlockType.ROOT_INDEX),
216         trailer.getMetaIndexCount());
217 
218     // File info
219     fileInfo = new FileInfo();
220     fileInfo.read(blockIter.nextBlockWithBlockType(BlockType.FILE_INFO).getByteStream());
221     byte[] creationTimeBytes = fileInfo.get(FileInfo.CREATE_TIME_TS);
222     this.hfileContext.setFileCreateTime(creationTimeBytes == null?  0:
223         Bytes.toLong(creationTimeBytes));
224     if (fileInfo.get(FileInfo.LASTKEY) != null) {
225       lastKeyCell = new KeyValue.KeyOnlyKeyValue(fileInfo.get(FileInfo.LASTKEY));
226     }
227     avgKeyLen = Bytes.toInt(fileInfo.get(FileInfo.AVG_KEY_LEN));
228     avgValueLen = Bytes.toInt(fileInfo.get(FileInfo.AVG_VALUE_LEN));
229     byte [] keyValueFormatVersion = fileInfo.get(HFileWriterImpl.KEY_VALUE_VERSION);
230     includesMemstoreTS = keyValueFormatVersion != null &&
231         Bytes.toInt(keyValueFormatVersion) == HFileWriterImpl.KEY_VALUE_VER_WITH_MEMSTORE;
232     fsBlockReader.setIncludesMemstoreTS(includesMemstoreTS);
233     if (includesMemstoreTS) {
234       decodeMemstoreTS = Bytes.toLong(fileInfo.get(HFileWriterImpl.MAX_MEMSTORE_TS_KEY)) > 0;
235     }
236 
237     // Read data block encoding algorithm name from file info.
238     dataBlockEncoder = HFileDataBlockEncoderImpl.createFromFileInfo(fileInfo);
239     fsBlockReader.setDataBlockEncoder(dataBlockEncoder);
240 
241     // Store all other load-on-open blocks for further consumption.
242     HFileBlock b;
243     while ((b = blockIter.nextBlock()) != null) {
244       loadOnOpenBlocks.add(b);
245     }
246 
247     // Prefetch file blocks upon open if requested
248     if (cacheConf.shouldPrefetchOnOpen()) {
249       PrefetchExecutor.request(path, new Runnable() {
250         public void run() {
251           try {
252             long offset = 0;
253             long end = fileSize - getTrailer().getTrailerSize();
254             HFileBlock prevBlock = null;
255             while (offset < end) {
256               if (Thread.interrupted()) {
257                 break;
258               }
259               long onDiskSize = -1;
260               if (prevBlock != null) {
261                 onDiskSize = prevBlock.getNextBlockOnDiskSizeWithHeader();
262               }
263               HFileBlock block = readBlock(offset, onDiskSize, true, false, false, false,
264                 null, null);
265               // Need not update the current block. Ideally here the readBlock won't find the
266               // block in cache. We call this readBlock so that block data is read from FS and
267               // cached in BC. So there is no reference count increment that happens here.
268               // The return will ideally be a noop because the block is not of MemoryType SHARED.
269               returnBlock(block);
270               prevBlock = block;
271               offset += block.getOnDiskSizeWithHeader();
272             }
273           } catch (IOException e) {
274             // IOExceptions are probably due to region closes (relocation, etc.)
275             if (LOG.isTraceEnabled()) {
276               LOG.trace("Exception encountered while prefetching " + path + ":", e);
277             }
278           } catch (Exception e) {
279             // Other exceptions are interesting
280             LOG.warn("Exception encountered while prefetching " + path + ":", e);
281           } finally {
282             PrefetchExecutor.complete(path);
283           }
284         }
285       });
286     }
287 
288     byte[] tmp = fileInfo.get(FileInfo.MAX_TAGS_LEN);
289     // max tag length is not present in the HFile means tags were not at all written to file.
290     if (tmp != null) {
291       hfileContext.setIncludesTags(true);
292       tmp = fileInfo.get(FileInfo.TAGS_COMPRESSED);
293       if (tmp != null && Bytes.toBoolean(tmp)) {
294         hfileContext.setCompressTags(true);
295       }
296     }
297   }
298 
299   /**
300    * File version check is a little sloppy. We read v3 files but can also read v2 files if their
301    * content has been pb'd; files written with 0.98.
302    */
303   private void checkFileVersion() {
304     int majorVersion = trailer.getMajorVersion();
305     if (majorVersion == getMajorVersion()) return;
306     int minorVersion = trailer.getMinorVersion();
307     if (majorVersion == 2 && minorVersion >= MIN_V2_MINOR_VERSION_WITH_PB) return;
308     // We can read v3 or v2 versions of hfile.
309     throw new IllegalArgumentException("Invalid HFile version: major=" +
310       trailer.getMajorVersion() + ", minor=" + trailer.getMinorVersion() + ": expected at least " +
311       "major=2 and minor=" + MAX_MINOR_VERSION);
312   }
313 
314   @SuppressWarnings("serial")
315   public static class BlockIndexNotLoadedException extends IllegalStateException {
316     public BlockIndexNotLoadedException() {
317       // Add a message in case anyone relies on it as opposed to class name.
318       super("Block index not loaded");
319     }
320   }
321 
322   private String toStringFirstKey() {
323     if(getFirstKey() == null)
324       return null;
325     return CellUtil.getCellKeyAsString(getFirstKey());
326   }
327 
328   private String toStringLastKey() {
329     return CellUtil.toString(getLastKey(), false);
330   }
331 
332   @Override
333   public String toString() {
334     return "reader=" + path.toString() +
335         (!isFileInfoLoaded()? "":
336           ", compression=" + compressAlgo.getName() +
337           ", cacheConf=" + cacheConf +
338           ", firstKey=" + toStringFirstKey() +
339           ", lastKey=" + toStringLastKey()) +
340           ", avgKeyLen=" + avgKeyLen +
341           ", avgValueLen=" + avgValueLen +
342           ", entries=" + trailer.getEntryCount() +
343           ", length=" + fileSize;
344   }
345 
346   @Override
347   public long length() {
348     return fileSize;
349   }
350 
351   @Override
352   public void returnBlock(HFileBlock block) {
353     BlockCache blockCache = this.cacheConf.getBlockCache();
354     if (blockCache != null && block != null) {
355       BlockCacheKey cacheKey = new BlockCacheKey(this.getFileContext().getHFileName(),
356           block.getOffset(), this.isPrimaryReplicaReader());
357       blockCache.returnBlock(cacheKey, block);
358     }
359   }
360   /**
361    * @return the first key in the file. May be null if file has no entries. Note
362    *         that this is not the first row key, but rather the byte form of the
363    *         first KeyValue.
364    */
365   @Override
366   public Cell getFirstKey() {
367     if (dataBlockIndexReader == null) {
368       throw new BlockIndexNotLoadedException();
369     }
370     return dataBlockIndexReader.isEmpty() ? null
371         : dataBlockIndexReader.getRootBlockKey(0);
372   }
373 
374   /**
375    * TODO left from {@link HFile} version 1: move this to StoreFile after Ryan's
376    * patch goes in to eliminate {@link KeyValue} here.
377    *
378    * @return the first row key, or null if the file is empty.
379    */
380   @Override
381   public byte[] getFirstRowKey() {
382     Cell firstKey = getFirstKey();
383     // We have to copy the row part to form the row key alone
384     return firstKey == null? null: CellUtil.cloneRow(firstKey);
385   }
386 
387   /**
388    * TODO left from {@link HFile} version 1: move this to StoreFile after
389    * Ryan's patch goes in to eliminate {@link KeyValue} here.
390    *
391    * @return the last row key, or null if the file is empty.
392    */
393   @Override
394   public byte[] getLastRowKey() {
395     Cell lastKey = getLastKey();
396     return lastKey == null? null: CellUtil.cloneRow(lastKey);
397   }
398 
399   /** @return number of KV entries in this HFile */
400   @Override
401   public long getEntries() {
402     return trailer.getEntryCount();
403   }
404 
405   /** @return comparator */
406   @Override
407   public CellComparator getComparator() {
408     return comparator;
409   }
410 
411   /** @return compression algorithm */
412   @Override
413   public Compression.Algorithm getCompressionAlgorithm() {
414     return compressAlgo;
415   }
416 
417   /**
418    * @return the total heap size of data and meta block indexes in bytes. Does
419    *         not take into account non-root blocks of a multilevel data index.
420    */
421   public long indexSize() {
422     return (dataBlockIndexReader != null ? dataBlockIndexReader.heapSize() : 0)
423         + ((metaBlockIndexReader != null) ? metaBlockIndexReader.heapSize()
424             : 0);
425   }
426 
427   @Override
428   public String getName() {
429     return name;
430   }
431 
432   @Override
433   public HFileBlockIndex.BlockIndexReader getDataBlockIndexReader() {
434     return dataBlockIndexReader;
435   }
436 
437   @Override
438   public FixedFileTrailer getTrailer() {
439     return trailer;
440   }
441 
442   @Override
443   public boolean isPrimaryReplicaReader() {
444     return isPrimaryReplicaReader;
445   }
446 
447   @Override
448   public void setPrimaryReplicaReader(boolean isPrimaryReplicaReader) {
449     this.isPrimaryReplicaReader = isPrimaryReplicaReader;
450   }
451 
452   @Override
453   public FileInfo loadFileInfo() throws IOException {
454     return fileInfo;
455   }
456 
457   /**
458    * An exception thrown when an operation requiring a scanner to be seeked
459    * is invoked on a scanner that is not seeked.
460    */
461   @SuppressWarnings("serial")
462   public static class NotSeekedException extends IllegalStateException {
463     public NotSeekedException() {
464       super("Not seeked to a key/value");
465     }
466   }
467 
468   protected static class HFileScannerImpl implements HFileScanner {
469     private ByteBuff blockBuffer;
470     protected final boolean cacheBlocks;
471     protected final boolean pread;
472     protected final boolean isCompaction;
473     private int currKeyLen;
474     private int currValueLen;
475     private int currMemstoreTSLen;
476     private long currMemstoreTS;
477     // Updated but never read?
478     protected volatile int blockFetches;
479     protected final HFile.Reader reader;
480     private int currTagsLen;
481     // buffer backed keyonlyKV
482     private ByteBufferedKeyOnlyKeyValue bufBackedKeyOnlyKv = new ByteBufferedKeyOnlyKeyValue();
483     // A pair for reusing in blockSeek() so that we don't garbage lot of objects
484     final ObjectIntPair<ByteBuffer> pair = new ObjectIntPair<ByteBuffer>();
485 
486     /**
487      * The next indexed key is to keep track of the indexed key of the next data block.
488      * If the nextIndexedKey is HConstants.NO_NEXT_INDEXED_KEY, it means that the
489      * current data block is the last data block.
490      *
491      * If the nextIndexedKey is null, it means the nextIndexedKey has not been loaded yet.
492      */
493     protected Cell nextIndexedKey;
494     // Current block being used
495     protected HFileBlock curBlock;
496     // Previous blocks that were used in the course of the read
497     protected final ArrayList<HFileBlock> prevBlocks = new ArrayList<HFileBlock>();
498 
499     public HFileScannerImpl(final HFile.Reader reader, final boolean cacheBlocks,
500         final boolean pread, final boolean isCompaction) {
501       this.reader = reader;
502       this.cacheBlocks = cacheBlocks;
503       this.pread = pread;
504       this.isCompaction = isCompaction;
505     }
506 
507     void updateCurrBlockRef(HFileBlock block) {
508       if (block != null && this.curBlock != null &&
509           block.getOffset() == this.curBlock.getOffset()) {
510         return;
511       }
512       // We don't have to keep ref to EXCLUSIVE type of block
513       if (this.curBlock != null && this.curBlock.usesSharedMemory()) {
514         prevBlocks.add(this.curBlock);
515       }
516       this.curBlock = block;
517     }
518 
519     void reset() {
520       // We don't have to keep ref to EXCLUSIVE type of block
521       if (this.curBlock != null && this.curBlock.usesSharedMemory()) {
522         this.prevBlocks.add(this.curBlock);
523       }
524       this.curBlock = null;
525     }
526 
527     private void returnBlockToCache(HFileBlock block) {
528       if (LOG.isTraceEnabled()) {
529         LOG.trace("Returning the block : " + block);
530       }
531       this.reader.returnBlock(block);
532     }
533 
534     private void returnBlocks(boolean returnAll) {
535       for (int i = 0; i < this.prevBlocks.size(); i++) {
536         returnBlockToCache(this.prevBlocks.get(i));
537       }
538       this.prevBlocks.clear();
539       if (returnAll && this.curBlock != null) {
540         returnBlockToCache(this.curBlock);
541         this.curBlock = null;
542       }
543     }
544     @Override
545     public boolean isSeeked(){
546       return blockBuffer != null;
547     }
548 
549     @Override
550     public String toString() {
551       return "HFileScanner for reader " + String.valueOf(getReader());
552     }
553 
554     protected void assertSeeked() {
555       if (!isSeeked())
556         throw new NotSeekedException();
557     }
558 
559     @Override
560     public HFile.Reader getReader() {
561       return reader;
562     }
563 
564     protected int getCellBufSize() {
565       int kvBufSize = KEY_VALUE_LEN_SIZE + currKeyLen + currValueLen;
566       if (this.reader.getFileContext().isIncludesTags()) {
567         kvBufSize += Bytes.SIZEOF_SHORT + currTagsLen;
568       }
569       return kvBufSize;
570     }
571 
572     @Override
573     public void close() {
574       this.returnBlocks(true);
575     }
576 
577     protected int getCurCellSize() {
578       int curCellSize =  KEY_VALUE_LEN_SIZE + currKeyLen + currValueLen
579           + currMemstoreTSLen;
580       if (this.reader.getFileContext().isIncludesTags()) {
581         curCellSize += Bytes.SIZEOF_SHORT + currTagsLen;
582       }
583       return curCellSize;
584     }
585 
586     protected void readKeyValueLen() {
587       // This is a hot method. We go out of our way to make this method short so it can be
588       // inlined and is not too big to compile. We also manage position in ByteBuffer ourselves
589       // because it is faster than going via range-checked ByteBuffer methods or going through a
590       // byte buffer array a byte at a time.
591       // Get a long at a time rather than read two individual ints. In micro-benchmarking, even
592       // with the extra bit-fiddling, this is order-of-magnitude faster than getting two ints.
593       // Trying to imitate what was done - need to profile if this is better or
594       // earlier way is better by doing mark and reset?
595       // But ensure that you read long instead of two ints
596       long ll = blockBuffer.getLongAfterPosition(0);
597       // Read top half as an int of key length and bottom int as value length
598       this.currKeyLen = (int)(ll >> Integer.SIZE);
599       this.currValueLen = (int)(Bytes.MASK_FOR_LOWER_INT_IN_LONG ^ ll);
600       checkKeyValueLen();
601       // Move position past the key and value lengths and then beyond the key and value
602       int p = (Bytes.SIZEOF_LONG + currKeyLen + currValueLen);
603       if (reader.getFileContext().isIncludesTags()) {
604         // Tags length is a short.
605         this.currTagsLen = blockBuffer.getShortAfterPosition(p);
606         checkTagsLen();
607         p += (Bytes.SIZEOF_SHORT + currTagsLen);
608       }
609       readMvccVersion(p);
610     }
611 
612     private final void checkTagsLen() {
613       if (checkLen(this.currTagsLen)) {
614         throw new IllegalStateException("Invalid currTagsLen " + this.currTagsLen +
615           ". Block offset: " + curBlock.getOffset() + ", block length: " +
616             this.blockBuffer.limit() +
617           ", position: " + this.blockBuffer.position() + " (without header).");
618       }
619     }
620 
621     /**
622      * Read mvcc. Does checks to see if we even need to read the mvcc at all.
623      * @param offsetFromPos
624      */
625     protected void readMvccVersion(final int offsetFromPos) {
626       // See if we even need to decode mvcc.
627       if (!this.reader.shouldIncludeMemstoreTS()) return;
628       if (!this.reader.isDecodeMemstoreTS()) {
629         currMemstoreTS = 0;
630         currMemstoreTSLen = 1;
631         return;
632       }
633       _readMvccVersion(offsetFromPos);
634     }
635 
636     /**
637      * Actually do the mvcc read. Does no checks.
638      * @param offsetFromPos
639      */
640     private void _readMvccVersion(int offsetFromPos) {
641       // This is Bytes#bytesToVint inlined so can save a few instructions in this hot method; i.e.
642       // previous if one-byte vint, we'd redo the vint call to find int size.
643       // Also the method is kept small so can be inlined.
644       byte firstByte = blockBuffer.getByteAfterPosition(offsetFromPos);
645       int len = WritableUtils.decodeVIntSize(firstByte);
646       if (len == 1) {
647         this.currMemstoreTS = firstByte;
648       } else {
649         int remaining = len -1;
650         long i = 0;
651         offsetFromPos++;
652         if (remaining >= Bytes.SIZEOF_INT) {
653           i = blockBuffer.getIntAfterPosition(offsetFromPos);
654           remaining -= Bytes.SIZEOF_INT;
655           offsetFromPos += Bytes.SIZEOF_INT;
656         }
657         if (remaining >= Bytes.SIZEOF_SHORT) {
658           short s = blockBuffer.getShortAfterPosition(offsetFromPos);
659           i = i << 16;
660           i = i | (s & 0xFFFF);
661           remaining -= Bytes.SIZEOF_SHORT;
662           offsetFromPos += Bytes.SIZEOF_SHORT;
663         }
664         for (int idx = 0; idx < remaining; idx++) {
665           byte b = blockBuffer.getByteAfterPosition(offsetFromPos + idx);
666           i = i << 8;
667           i = i | (b & 0xFF);
668         }
669         currMemstoreTS = (WritableUtils.isNegativeVInt(firstByte) ? ~i : i);
670       }
671       this.currMemstoreTSLen = len;
672     }
673 
674     /**
675      * Within a loaded block, seek looking for the last key that is smaller than
676      * (or equal to?) the key we are interested in.
677      * A note on the seekBefore: if you have seekBefore = true, AND the first
678      * key in the block = key, then you'll get thrown exceptions. The caller has
679      * to check for that case and load the previous block as appropriate.
680      * @param key
681      *          the key to find
682      * @param seekBefore
683      *          find the key before the given key in case of exact match.
684      * @return 0 in case of an exact key match, 1 in case of an inexact match,
685      *         -2 in case of an inexact match and furthermore, the input key
686      *         less than the first key of current block(e.g. using a faked index
687      *         key)
688      */
689     protected int blockSeek(Cell key, boolean seekBefore) {
690       int klen, vlen, tlen = 0;
691       int lastKeyValueSize = -1;
692       int offsetFromPos;
693       do {
694         offsetFromPos = 0;
695         // Better to ensure that we use the BB Utils here
696         long ll = blockBuffer.getLongAfterPosition(offsetFromPos);
697         klen = (int)(ll >> Integer.SIZE);
698         vlen = (int)(Bytes.MASK_FOR_LOWER_INT_IN_LONG ^ ll);
699         if (klen < 0 || vlen < 0 || klen > blockBuffer.limit()
700             || vlen > blockBuffer.limit()) {
701           throw new IllegalStateException("Invalid klen " + klen + " or vlen "
702               + vlen + ". Block offset: "
703               + curBlock.getOffset() + ", block length: " + blockBuffer.limit() + ", position: "
704               + blockBuffer.position() + " (without header).");
705         }
706         offsetFromPos += Bytes.SIZEOF_LONG;
707         blockBuffer.asSubByteBuffer(blockBuffer.position() + offsetFromPos, klen, pair);
708         bufBackedKeyOnlyKv.setKey(pair.getFirst(), pair.getSecond(), klen);
709         int comp = reader.getComparator().compareKeyIgnoresMvcc(key, bufBackedKeyOnlyKv);
710         offsetFromPos += klen + vlen;
711         if (this.reader.getFileContext().isIncludesTags()) {
712           // Read short as unsigned, high byte first
713           tlen = ((blockBuffer.getByteAfterPosition(offsetFromPos) & 0xff) << 8)
714               ^ (blockBuffer.getByteAfterPosition(offsetFromPos + 1) & 0xff);
715           if (tlen < 0 || tlen > blockBuffer.limit()) {
716             throw new IllegalStateException("Invalid tlen " + tlen + ". Block offset: "
717                 + curBlock.getOffset() + ", block length: " + blockBuffer.limit() + ", position: "
718                 + blockBuffer.position() + " (without header).");
719           }
720           // add the two bytes read for the tags.
721           offsetFromPos += tlen + (Bytes.SIZEOF_SHORT);
722         }
723         if (this.reader.shouldIncludeMemstoreTS()) {
724           // Directly read the mvcc based on current position
725           readMvccVersion(offsetFromPos);
726         }
727         if (comp == 0) {
728           if (seekBefore) {
729             if (lastKeyValueSize < 0) {
730               throw new IllegalStateException("blockSeek with seekBefore "
731                   + "at the first key of the block: key=" + CellUtil.getCellKeyAsString(key)
732                   + ", blockOffset=" + curBlock.getOffset() + ", onDiskSize="
733                   + curBlock.getOnDiskSizeWithHeader());
734             }
735             blockBuffer.moveBack(lastKeyValueSize);
736             readKeyValueLen();
737             return 1; // non exact match.
738           }
739           currKeyLen = klen;
740           currValueLen = vlen;
741           currTagsLen = tlen;
742           return 0; // indicate exact match
743         } else if (comp < 0) {
744           if (lastKeyValueSize > 0) {
745             blockBuffer.moveBack(lastKeyValueSize);
746           }
747           readKeyValueLen();
748           if (lastKeyValueSize == -1 && blockBuffer.position() == 0) {
749             return HConstants.INDEX_KEY_MAGIC;
750           }
751           return 1;
752         }
753         // The size of this key/value tuple, including key/value length fields.
754         lastKeyValueSize = klen + vlen + currMemstoreTSLen + KEY_VALUE_LEN_SIZE;
755         // include tag length also if tags included with KV
756         if (reader.getFileContext().isIncludesTags()) {
757           lastKeyValueSize += tlen + Bytes.SIZEOF_SHORT;
758         }
759         blockBuffer.skip(lastKeyValueSize);
760       } while (blockBuffer.hasRemaining());
761 
762       // Seek to the last key we successfully read. This will happen if this is
763       // the last key/value pair in the file, in which case the following call
764       // to next() has to return false.
765       blockBuffer.moveBack(lastKeyValueSize);
766       readKeyValueLen();
767       return 1; // didn't exactly find it.
768     }
769 
770     @Override
771     public Cell getNextIndexedKey() {
772       return nextIndexedKey;
773     }
774 
775     @Override
776     public int seekTo(Cell key) throws IOException {
777       return seekTo(key, true);
778     }
779 
780     @Override
781     public int reseekTo(Cell key) throws IOException {
782       int compared;
783       if (isSeeked()) {
784         compared = compareKey(reader.getComparator(), key);
785         if (compared < 1) {
786           // If the required key is less than or equal to current key, then
787           // don't do anything.
788           return compared;
789         } else {
790           // The comparison with no_next_index_key has to be checked
791           if (this.nextIndexedKey != null &&
792               (this.nextIndexedKey == HConstants.NO_NEXT_INDEXED_KEY || reader
793               .getComparator().compareKeyIgnoresMvcc(key, nextIndexedKey) < 0)) {
794             // The reader shall continue to scan the current data block instead
795             // of querying the
796             // block index as long as it knows the target key is strictly
797             // smaller than
798             // the next indexed key or the current data block is the last data
799             // block.
800             return loadBlockAndSeekToKey(this.curBlock, nextIndexedKey, false, key,
801                 false);
802           }
803 
804         }
805       }
806       // Don't rewind on a reseek operation, because reseek implies that we are
807       // always going forward in the file.
808       return seekTo(key, false);
809     }
810 
811     /**
812      * An internal API function. Seek to the given key, optionally rewinding to
813      * the first key of the block before doing the seek.
814      *
815      * @param key - a cell representing the key that we need to fetch
816      * @param rewind whether to rewind to the first key of the block before
817      *        doing the seek. If this is false, we are assuming we never go
818      *        back, otherwise the result is undefined.
819      * @return -1 if the key is earlier than the first key of the file,
820      *         0 if we are at the given key, 1 if we are past the given key
821      *         -2 if the key is earlier than the first key of the file while
822      *         using a faked index key
823      * @throws IOException
824      */
825     public int seekTo(Cell key, boolean rewind) throws IOException {
826       HFileBlockIndex.BlockIndexReader indexReader = reader.getDataBlockIndexReader();
827       BlockWithScanInfo blockWithScanInfo = indexReader.loadDataBlockWithScanInfo(key, curBlock,
828           cacheBlocks, pread, isCompaction, getEffectiveDataBlockEncoding());
829       if (blockWithScanInfo == null || blockWithScanInfo.getHFileBlock() == null) {
830         // This happens if the key e.g. falls before the beginning of the
831         // file.
832         return -1;
833       }
834       return loadBlockAndSeekToKey(blockWithScanInfo.getHFileBlock(),
835           blockWithScanInfo.getNextIndexedKey(), rewind, key, false);
836     }
837 
838     @Override
839     public boolean seekBefore(Cell key) throws IOException {
840       HFileBlock seekToBlock = reader.getDataBlockIndexReader().seekToDataBlock(key, curBlock,
841           cacheBlocks, pread, isCompaction, reader.getEffectiveEncodingInCache(isCompaction));
842       if (seekToBlock == null) {
843         return false;
844       }
845       Cell firstKey = getFirstKeyCellInBlock(seekToBlock);
846       if (reader.getComparator()
847            .compareKeyIgnoresMvcc(firstKey, key) >= 0) {
848         long previousBlockOffset = seekToBlock.getPrevBlockOffset();
849         // The key we are interested in
850         if (previousBlockOffset == -1) {
851           // we have a 'problem', the key we want is the first of the file.
852           return false;
853         }
854 
855         // The first key in the current block 'seekToBlock' is greater than the given 
856         // seekBefore key. We will go ahead by reading the next block that satisfies the
857         // given key. Return the current block before reading the next one.
858         reader.returnBlock(seekToBlock);
859         // It is important that we compute and pass onDiskSize to the block
860         // reader so that it does not have to read the header separately to
861         // figure out the size.  Currently, we do not have a way to do this
862         // correctly in the general case however.
863         // TODO: See https://issues.apache.org/jira/browse/HBASE-14576
864         int prevBlockSize = -1;
865         seekToBlock = reader.readBlock(previousBlockOffset,
866             prevBlockSize, cacheBlocks,
867             pread, isCompaction, true, BlockType.DATA, getEffectiveDataBlockEncoding());
868         // TODO shortcut: seek forward in this block to the last key of the
869         // block.
870       }
871       loadBlockAndSeekToKey(seekToBlock, firstKey, true, key, true);
872       return true;
873     }
874 
875     /**
876      * Scans blocks in the "scanned" section of the {@link HFile} until the next
877      * data block is found.
878      *
879      * @return the next block, or null if there are no more data blocks
880      * @throws IOException
881      */
882     protected HFileBlock readNextDataBlock() throws IOException {
883       long lastDataBlockOffset = reader.getTrailer().getLastDataBlockOffset();
884       if (curBlock == null)
885         return null;
886 
887       HFileBlock block = this.curBlock;
888 
889       do {
890         if (block.getOffset() >= lastDataBlockOffset)
891           return null;
892 
893         if (block.getOffset() < 0) {
894           throw new IOException("Invalid block file offset: " + block);
895         }
896 
897         // We are reading the next block without block type validation, because
898         // it might turn out to be a non-data block.
899         block = reader.readBlock(block.getOffset()
900             + block.getOnDiskSizeWithHeader(),
901             block.getNextBlockOnDiskSizeWithHeader(), cacheBlocks, pread,
902             isCompaction, true, null, getEffectiveDataBlockEncoding());
903         if (block != null && !block.getBlockType().isData()) {
904           // Whatever block we read we will be returning it unless
905           // it is a datablock. Just in case the blocks are non data blocks
906           reader.returnBlock(block);
907         }
908       } while (!block.getBlockType().isData());
909 
910       return block;
911     }
912 
913     public DataBlockEncoding getEffectiveDataBlockEncoding() {
914       return this.reader.getEffectiveEncodingInCache(isCompaction);
915     }
916 
917     @Override
918     public Cell getCell() {
919       if (!isSeeked())
920         return null;
921 
922       Cell ret;
923       int cellBufSize = getCellBufSize();
924       long seqId = 0l;
925       if (this.reader.shouldIncludeMemstoreTS()) {
926         seqId = currMemstoreTS;
927       }
928       if (blockBuffer.hasArray()) {
929         // TODO : reduce the varieties of KV here. Check if based on a boolean
930         // we can handle the 'no tags' case.
931         if (currTagsLen > 0) {
932           if (this.curBlock.usesSharedMemory()) {
933             ret = new ShareableMemoryKeyValue(blockBuffer.array(), blockBuffer.arrayOffset()
934               + blockBuffer.position(), getCellBufSize(), seqId);
935           } else {
936             ret = new SizeCachedKeyValue(blockBuffer.array(), blockBuffer.arrayOffset()
937                     + blockBuffer.position(), cellBufSize, seqId);
938           }
939         } else {
940           if (this.curBlock.usesSharedMemory()) {
941             ret = new ShareableMemoryNoTagsKeyValue(blockBuffer.array(), blockBuffer.arrayOffset()
942                     + blockBuffer.position(), getCellBufSize(), seqId);
943           } else {
944             ret = new SizeCachedNoTagsKeyValue(blockBuffer.array(), blockBuffer.arrayOffset()
945                     + blockBuffer.position(), cellBufSize, seqId);
946           }
947         }
948       } else {
949         ByteBuffer buf = blockBuffer.asSubByteBuffer(cellBufSize);
950         if (buf.isDirect()) {
951           if (this.curBlock.usesSharedMemory()) {
952             ret = new ShareableMemoryOffheapKeyValue(buf, buf.position(), cellBufSize,
953                 currTagsLen > 0, seqId);
954           } else {
955             ret = new OffheapKeyValue(buf, buf.position(), cellBufSize, currTagsLen > 0, seqId);
956           }
957         } else {
958           if (this.curBlock.usesSharedMemory()) {
959             if (currTagsLen > 0) {
960               ret = new ShareableMemoryKeyValue(buf.array(), buf.arrayOffset() + buf.position(),
961                   cellBufSize, seqId);
962             } else {
963               ret = new ShareableMemoryNoTagsKeyValue(buf.array(),
964                   buf.arrayOffset() + buf.position(), cellBufSize, seqId);
965             }
966           } else {
967             if (currTagsLen > 0) {
968               ret = new SizeCachedKeyValue(buf.array(), buf.arrayOffset() + buf.position(),
969                   cellBufSize, seqId);
970             } else {
971               ret = new SizeCachedNoTagsKeyValue(buf.array(), buf.arrayOffset() + buf.position(),
972                   cellBufSize, seqId);
973             }
974           }
975         }
976       }
977       return ret;
978     }
979 
980     @Override
981     public Cell getKey() {
982       assertSeeked();
983       // Create a new object so that this getKey is cached as firstKey, lastKey
984       ObjectIntPair<ByteBuffer> keyPair = new ObjectIntPair<ByteBuffer>();
985       blockBuffer.asSubByteBuffer(blockBuffer.position() + KEY_VALUE_LEN_SIZE, currKeyLen, keyPair);
986       ByteBuffer keyBuf = keyPair.getFirst();
987       if (keyBuf.hasArray()) {
988         return new KeyValue.KeyOnlyKeyValue(keyBuf.array(), keyBuf.arrayOffset()
989             + keyPair.getSecond(), currKeyLen);
990       } else {
991         return new ByteBufferedKeyOnlyKeyValue(keyBuf, keyPair.getSecond(), currKeyLen);
992       }
993     }
994 
995     private static class ShareableMemoryKeyValue extends SizeCachedKeyValue implements
996         ShareableMemory {
997       public ShareableMemoryKeyValue(byte[] bytes, int offset, int length, long seqId) {
998         super(bytes, offset, length, seqId);
999       }
1000 
1001       @Override
1002       public Cell cloneToCell() {
1003         byte[] copy = Bytes.copy(this.bytes, this.offset, this.length);
1004         return new SizeCachedKeyValue(copy, 0, copy.length, getSequenceId());
1005       }
1006     }
1007 
1008     private static class ShareableMemoryNoTagsKeyValue extends SizeCachedNoTagsKeyValue implements
1009         ShareableMemory {
1010       public ShareableMemoryNoTagsKeyValue(byte[] bytes, int offset, int length, long seqId) {
1011         super(bytes, offset, length, seqId);
1012       }
1013 
1014       @Override
1015       public Cell cloneToCell() {
1016         byte[] copy = Bytes.copy(this.bytes, this.offset, this.length);
1017         return new SizeCachedNoTagsKeyValue(copy, 0, copy.length, getSequenceId());
1018       }
1019     }
1020 
1021     private static class ShareableMemoryOffheapKeyValue extends OffheapKeyValue implements
1022         ShareableMemory {
1023       public ShareableMemoryOffheapKeyValue(ByteBuffer buf, int offset, int length,
1024           boolean hasTags, long seqId) {
1025         super(buf, offset, length, hasTags, seqId);
1026       }
1027 
1028       @Override
1029       public Cell cloneToCell() {
1030         byte[] copy = new byte[this.length];
1031         ByteBufferUtils.copyFromBufferToArray(copy, this.buf, this.offset, 0, this.length);
1032         return new SizeCachedKeyValue(copy, 0, copy.length, getSequenceId());
1033       }
1034     }
1035 
1036     @Override
1037     public ByteBuffer getValue() {
1038       assertSeeked();
1039       // Okie to create new Pair. Not used in hot path
1040       ObjectIntPair<ByteBuffer> valuePair = new ObjectIntPair<ByteBuffer>();
1041       this.blockBuffer.asSubByteBuffer(blockBuffer.position() + KEY_VALUE_LEN_SIZE + currKeyLen,
1042         currValueLen, valuePair);
1043       ByteBuffer valBuf = valuePair.getFirst().duplicate();
1044       valBuf.position(valuePair.getSecond());
1045       valBuf.limit(currValueLen + valuePair.getSecond());
1046       return valBuf.slice();
1047     }
1048 
1049     protected void setNonSeekedState() {
1050       reset();
1051       blockBuffer = null;
1052       currKeyLen = 0;
1053       currValueLen = 0;
1054       currMemstoreTS = 0;
1055       currMemstoreTSLen = 0;
1056       currTagsLen = 0;
1057     }
1058 
1059     /**
1060      * Set the position on current backing blockBuffer.
1061      */
1062     private void positionThisBlockBuffer() {
1063       try {
1064         blockBuffer.skip(getCurCellSize());
1065       } catch (IllegalArgumentException e) {
1066         LOG.error("Current pos = " + blockBuffer.position()
1067             + "; currKeyLen = " + currKeyLen + "; currValLen = "
1068             + currValueLen + "; block limit = " + blockBuffer.limit()
1069             + "; HFile name = " + reader.getName()
1070             + "; currBlock currBlockOffset = " + this.curBlock.getOffset());
1071         throw e;
1072       }
1073     }
1074 
1075     /**
1076      * Set our selves up for the next 'next' invocation, set up next block.
1077      * @return True is more to read else false if at the end.
1078      * @throws IOException
1079      */
1080     private boolean positionForNextBlock() throws IOException {
1081       // Methods are small so they get inlined because they are 'hot'.
1082       long lastDataBlockOffset = reader.getTrailer().getLastDataBlockOffset();
1083       if (this.curBlock.getOffset() >= lastDataBlockOffset) {
1084         setNonSeekedState();
1085         return false;
1086       }
1087       return isNextBlock();
1088     }
1089 
1090 
1091     private boolean isNextBlock() throws IOException {
1092       // Methods are small so they get inlined because they are 'hot'.
1093       HFileBlock nextBlock = readNextDataBlock();
1094       if (nextBlock == null) {
1095         setNonSeekedState();
1096         return false;
1097       }
1098       updateCurrentBlock(nextBlock);
1099       return true;
1100     }
1101 
1102     private final boolean _next() throws IOException {
1103       // Small method so can be inlined. It is a hot one.
1104       if (blockBuffer.remaining() <= 0) {
1105         return positionForNextBlock();
1106       }
1107 
1108       // We are still in the same block.
1109       readKeyValueLen();
1110       return true;
1111     }
1112 
1113     /**
1114      * Go to the next key/value in the block section. Loads the next block if
1115      * necessary. If successful, {@link #getKey()} and {@link #getValue()} can
1116      * be called.
1117      *
1118      * @return true if successfully navigated to the next key/value
1119      */
1120     @Override
1121     public boolean next() throws IOException {
1122       // This is a hot method so extreme measures taken to ensure it is small and inlineable.
1123       // Checked by setting: -XX:+UnlockDiagnosticVMOptions -XX:+PrintInlining -XX:+PrintCompilation
1124       assertSeeked();
1125       positionThisBlockBuffer();
1126       return _next();
1127     }
1128 
1129     /**
1130      * Positions this scanner at the start of the file.
1131      *
1132      * @return false if empty file; i.e. a call to next would return false and
1133      *         the current key and value are undefined.
1134      * @throws IOException
1135      */
1136     @Override
1137     public boolean seekTo() throws IOException {
1138       if (reader == null) {
1139         return false;
1140       }
1141 
1142       if (reader.getTrailer().getEntryCount() == 0) {
1143         // No data blocks.
1144         return false;
1145       }
1146 
1147       long firstDataBlockOffset = reader.getTrailer().getFirstDataBlockOffset();
1148       if (curBlock != null
1149           && curBlock.getOffset() == firstDataBlockOffset) {
1150         return processFirstDataBlock();
1151       }
1152 
1153       readAndUpdateNewBlock(firstDataBlockOffset);
1154       return true;
1155     }
1156 
1157     protected boolean processFirstDataBlock() throws IOException{
1158       blockBuffer.rewind();
1159       readKeyValueLen();
1160       return true;
1161     }
1162 
1163     protected void readAndUpdateNewBlock(long firstDataBlockOffset) throws IOException,
1164         CorruptHFileException {
1165       HFileBlock newBlock = reader.readBlock(firstDataBlockOffset, -1, cacheBlocks, pread,
1166           isCompaction, true, BlockType.DATA, getEffectiveDataBlockEncoding());
1167       if (newBlock.getOffset() < 0) {
1168         throw new IOException("Invalid block offset: " + newBlock.getOffset());
1169       }
1170       updateCurrentBlock(newBlock);
1171     }
1172 
1173     protected int loadBlockAndSeekToKey(HFileBlock seekToBlock, Cell nextIndexedKey,
1174         boolean rewind, Cell key, boolean seekBefore) throws IOException {
1175       if (this.curBlock == null
1176           || this.curBlock.getOffset() != seekToBlock.getOffset()) {
1177         updateCurrentBlock(seekToBlock);
1178       } else if (rewind) {
1179         blockBuffer.rewind();
1180       }
1181 
1182       // Update the nextIndexedKey
1183       this.nextIndexedKey = nextIndexedKey;
1184       return blockSeek(key, seekBefore);
1185     }
1186 
1187     /**
1188      * @param v
1189      * @return True if v &lt; 0 or v &gt; current block buffer limit.
1190      */
1191     protected final boolean checkLen(final int v) {
1192       return v < 0 || v > this.blockBuffer.limit();
1193     }
1194 
1195     /**
1196      * Check key and value lengths are wholesome.
1197      */
1198     protected final void checkKeyValueLen() {
1199       if (checkLen(this.currKeyLen) || checkLen(this.currValueLen)) {
1200         throw new IllegalStateException("Invalid currKeyLen " + this.currKeyLen
1201             + " or currValueLen " + this.currValueLen + ". Block offset: "
1202             + this.curBlock.getOffset() + ", block length: "
1203             + this.blockBuffer.limit() + ", position: " + this.blockBuffer.position()
1204             + " (without header).");
1205       }
1206     }
1207 
1208     /**
1209      * Updates the current block to be the given {@link HFileBlock}. Seeks to
1210      * the the first key/value pair.
1211      *
1212      * @param newBlock the block to make current
1213      */
1214     protected void updateCurrentBlock(HFileBlock newBlock) throws IOException {
1215       // Set the active block on the reader
1216       // sanity check
1217       if (newBlock.getBlockType() != BlockType.DATA) {
1218         throw new IllegalStateException("ScannerV2 works only on data " + "blocks, got "
1219             + newBlock.getBlockType() + "; " + "fileName=" + reader.getName()
1220             + ", " + "dataBlockEncoder=" + reader.getDataBlockEncoding() + ", " + "isCompaction="
1221             + isCompaction);
1222       }
1223 
1224       updateCurrBlockRef(newBlock);
1225       blockBuffer = newBlock.getBufferWithoutHeader();
1226       readKeyValueLen();
1227       blockFetches++;
1228 
1229       // Reset the next indexed key
1230       this.nextIndexedKey = null;
1231     }
1232 
1233     protected Cell getFirstKeyCellInBlock(HFileBlock curBlock) {
1234       ByteBuff buffer = curBlock.getBufferWithoutHeader();
1235       // It is safe to manipulate this buffer because we own the buffer object.
1236       buffer.rewind();
1237       int klen = buffer.getInt();
1238       buffer.skip(Bytes.SIZEOF_INT);// Skip value len part
1239       ByteBuffer keyBuff = buffer.asSubByteBuffer(klen);
1240       if (keyBuff.hasArray()) {
1241         return new KeyValue.KeyOnlyKeyValue(keyBuff.array(), keyBuff.arrayOffset()
1242             + keyBuff.position(), klen);
1243       } else {
1244         return new ByteBufferedKeyOnlyKeyValue(keyBuff, keyBuff.position(), klen);
1245       }
1246     }
1247 
1248     @Override
1249     public String getKeyString() {
1250       return CellUtil.toString(getKey(), false);
1251     }
1252 
1253     @Override
1254     public String getValueString() {
1255       return ByteBufferUtils.toStringBinary(getValue());
1256     }
1257 
1258     public int compareKey(CellComparator comparator, Cell key) {
1259       blockBuffer.asSubByteBuffer(blockBuffer.position() + KEY_VALUE_LEN_SIZE, currKeyLen, pair);
1260       this.bufBackedKeyOnlyKv.setKey(pair.getFirst(), pair.getSecond(), currKeyLen);
1261       return comparator.compareKeyIgnoresMvcc(key, this.bufBackedKeyOnlyKv);
1262     }
1263 
1264     @Override
1265     public void shipped() throws IOException {
1266       this.returnBlocks(false);
1267     }
1268   }
1269 
1270   public Path getPath() {
1271     return path;
1272   }
1273 
1274   @Override
1275   public DataBlockEncoding getDataBlockEncoding() {
1276     return dataBlockEncoder.getDataBlockEncoding();
1277   }
1278 
1279   @Override
1280   public Configuration getConf() {
1281     return conf;
1282   }
1283 
1284   @Override
1285   public void setConf(Configuration conf) {
1286     this.conf = conf;
1287   }
1288 
1289   /** Minor versions in HFile starting with this number have hbase checksums */
1290   public static final int MINOR_VERSION_WITH_CHECKSUM = 1;
1291   /** In HFile minor version that does not support checksums */
1292   public static final int MINOR_VERSION_NO_CHECKSUM = 0;
1293 
1294   /** HFile minor version that introduced pbuf filetrailer */
1295   public static final int PBUF_TRAILER_MINOR_VERSION = 2;
1296 
1297   /**
1298    * The size of a (key length, value length) tuple that prefixes each entry in
1299    * a data block.
1300    */
1301   public final static int KEY_VALUE_LEN_SIZE = 2 * Bytes.SIZEOF_INT;
1302 
1303   private boolean includesMemstoreTS = false;
1304   protected boolean decodeMemstoreTS = false;
1305 
1306 
1307   public boolean isDecodeMemstoreTS() {
1308     return this.decodeMemstoreTS;
1309   }
1310 
1311   public boolean shouldIncludeMemstoreTS() {
1312     return includesMemstoreTS;
1313   }
1314 
1315   /**
1316    * Retrieve block from cache. Validates the retrieved block's type vs {@code expectedBlockType}
1317    * and its encoding vs. {@code expectedDataBlockEncoding}. Unpacks the block as necessary.
1318    */
1319    private HFileBlock getCachedBlock(BlockCacheKey cacheKey, boolean cacheBlock, boolean useLock,
1320        boolean isCompaction, boolean updateCacheMetrics, BlockType expectedBlockType,
1321        DataBlockEncoding expectedDataBlockEncoding) throws IOException {
1322      // Check cache for block. If found return.
1323      if (cacheConf.isBlockCacheEnabled()) {
1324        BlockCache cache = cacheConf.getBlockCache();
1325        HFileBlock cachedBlock = (HFileBlock) cache.getBlock(cacheKey, cacheBlock, useLock,
1326          updateCacheMetrics);
1327        if (cachedBlock != null) {
1328          if (cacheConf.shouldCacheCompressed(cachedBlock.getBlockType().getCategory())) {
1329            HFileBlock compressedBlock = cachedBlock;
1330            cachedBlock = compressedBlock.unpack(hfileContext, fsBlockReader);
1331            // In case of compressed block after unpacking we can return the compressed block
1332           if (compressedBlock != cachedBlock) {
1333             cache.returnBlock(cacheKey, compressedBlock);
1334           }
1335         }
1336          validateBlockType(cachedBlock, expectedBlockType);
1337 
1338          if (expectedDataBlockEncoding == null) {
1339            return cachedBlock;
1340          }
1341          DataBlockEncoding actualDataBlockEncoding =
1342                  cachedBlock.getDataBlockEncoding();
1343          // Block types other than data blocks always have
1344          // DataBlockEncoding.NONE. To avoid false negative cache misses, only
1345          // perform this check if cached block is a data block.
1346          if (cachedBlock.getBlockType().isData() &&
1347                  !actualDataBlockEncoding.equals(expectedDataBlockEncoding)) {
1348            // This mismatch may happen if a Scanner, which is used for say a
1349            // compaction, tries to read an encoded block from the block cache.
1350            // The reverse might happen when an EncodedScanner tries to read
1351            // un-encoded blocks which were cached earlier.
1352            //
1353            // Because returning a data block with an implicit BlockType mismatch
1354            // will cause the requesting scanner to throw a disk read should be
1355            // forced here. This will potentially cause a significant number of
1356            // cache misses, so update so we should keep track of this as it might
1357            // justify the work on a CompoundScanner.
1358            if (!expectedDataBlockEncoding.equals(DataBlockEncoding.NONE) &&
1359                    !actualDataBlockEncoding.equals(DataBlockEncoding.NONE)) {
1360              // If the block is encoded but the encoding does not match the
1361              // expected encoding it is likely the encoding was changed but the
1362              // block was not yet evicted. Evictions on file close happen async
1363              // so blocks with the old encoding still linger in cache for some
1364              // period of time. This event should be rare as it only happens on
1365              // schema definition change.
1366              LOG.info("Evicting cached block with key " + cacheKey +
1367                      " because of a data block encoding mismatch" +
1368                      "; expected: " + expectedDataBlockEncoding +
1369                      ", actual: " + actualDataBlockEncoding);
1370              // This is an error scenario. so here we need to decrement the
1371              // count.
1372              cache.returnBlock(cacheKey, cachedBlock);
1373              cache.evictBlock(cacheKey);
1374            }
1375            return null;
1376          }
1377          return cachedBlock;
1378        }
1379      }
1380      return null;
1381    }
1382 
1383   /**
1384    * @param metaBlockName
1385    * @param cacheBlock Add block to cache, if found
1386    * @return block wrapped in a ByteBuffer, with header skipped
1387    * @throws IOException
1388    */
1389   @Override
1390   public HFileBlock getMetaBlock(String metaBlockName, boolean cacheBlock)
1391       throws IOException {
1392     if (trailer.getMetaIndexCount() == 0) {
1393       return null; // there are no meta blocks
1394     }
1395     if (metaBlockIndexReader == null) {
1396       throw new IOException("Meta index not loaded");
1397     }
1398 
1399     byte[] mbname = Bytes.toBytes(metaBlockName);
1400     int block = metaBlockIndexReader.rootBlockContainingKey(mbname,
1401         0, mbname.length);
1402     if (block == -1)
1403       return null;
1404     long blockSize = metaBlockIndexReader.getRootBlockDataSize(block);
1405 
1406     // Per meta key from any given file, synchronize reads for said block. This
1407     // is OK to do for meta blocks because the meta block index is always
1408     // single-level.
1409     synchronized (metaBlockIndexReader
1410         .getRootBlockKey(block)) {
1411       // Check cache for block. If found return.
1412       long metaBlockOffset = metaBlockIndexReader.getRootBlockOffset(block);
1413       BlockCacheKey cacheKey = new BlockCacheKey(name, metaBlockOffset,
1414         this.isPrimaryReplicaReader());
1415 
1416       cacheBlock &= cacheConf.shouldCacheDataOnRead();
1417       if (cacheConf.isBlockCacheEnabled()) {
1418         HFileBlock cachedBlock = getCachedBlock(cacheKey, cacheBlock, false, true, true,
1419           BlockType.META, null);
1420         if (cachedBlock != null) {
1421           assert cachedBlock.isUnpacked() : "Packed block leak.";
1422           // Return a distinct 'shallow copy' of the block,
1423           // so pos does not get messed by the scanner
1424           return cachedBlock;
1425         }
1426         // Cache Miss, please load.
1427       }
1428 
1429       HFileBlock metaBlock = fsBlockReader.readBlockData(metaBlockOffset,
1430           blockSize, -1, true).unpack(hfileContext, fsBlockReader);
1431 
1432       // Cache the block
1433       if (cacheBlock) {
1434         cacheConf.getBlockCache().cacheBlock(cacheKey, metaBlock,
1435             cacheConf.isInMemory(), this.cacheConf.isCacheDataInL1());
1436       }
1437 
1438       return metaBlock;
1439     }
1440   }
1441 
1442   @Override
1443   public HFileBlock readBlock(long dataBlockOffset, long onDiskBlockSize,
1444       final boolean cacheBlock, boolean pread, final boolean isCompaction,
1445       boolean updateCacheMetrics, BlockType expectedBlockType,
1446       DataBlockEncoding expectedDataBlockEncoding)
1447       throws IOException {
1448     if (dataBlockIndexReader == null) {
1449       throw new IOException("Block index not loaded");
1450     }
1451     if (dataBlockOffset < 0 || dataBlockOffset >= trailer.getLoadOnOpenDataOffset()) {
1452       throw new IOException("Requested block is out of range: " + dataBlockOffset +
1453         ", lastDataBlockOffset: " + trailer.getLastDataBlockOffset());
1454     }
1455     // For any given block from any given file, synchronize reads for said
1456     // block.
1457     // Without a cache, this synchronizing is needless overhead, but really
1458     // the other choice is to duplicate work (which the cache would prevent you
1459     // from doing).
1460 
1461     BlockCacheKey cacheKey = new BlockCacheKey(name, dataBlockOffset,
1462       this.isPrimaryReplicaReader());
1463 
1464     boolean useLock = false;
1465     IdLock.Entry lockEntry = null;
1466     TraceScope traceScope = Trace.startSpan("HFileReaderImpl.readBlock");
1467     try {
1468       while (true) {
1469         // Check cache for block. If found return.
1470         if (cacheConf.shouldReadBlockFromCache(expectedBlockType)) {
1471           if (useLock) {
1472             lockEntry = offsetLock.getLockEntry(dataBlockOffset);
1473           }
1474           // Try and get the block from the block cache. If the useLock variable is true then this
1475           // is the second time through the loop and it should not be counted as a block cache miss.
1476           HFileBlock cachedBlock = getCachedBlock(cacheKey, cacheBlock, useLock, isCompaction,
1477             updateCacheMetrics, expectedBlockType, expectedDataBlockEncoding);
1478           if (cachedBlock != null) {
1479             if (Trace.isTracing()) {
1480               traceScope.getSpan().addTimelineAnnotation("blockCacheHit");
1481             }
1482             assert cachedBlock.isUnpacked() : "Packed block leak.";
1483             if (cachedBlock.getBlockType().isData()) {
1484               if (updateCacheMetrics) {
1485                 HFile.dataBlockReadCnt.incrementAndGet();
1486               }
1487               // Validate encoding type for data blocks. We include encoding
1488               // type in the cache key, and we expect it to match on a cache hit.
1489               if (cachedBlock.getDataBlockEncoding() != dataBlockEncoder.getDataBlockEncoding()) {
1490                 throw new IOException("Cached block under key " + cacheKey + " "
1491                   + "has wrong encoding: " + cachedBlock.getDataBlockEncoding() + " (expected: "
1492                   + dataBlockEncoder.getDataBlockEncoding() + ")");
1493               }
1494             }
1495             // Cache-hit. Return!
1496             return cachedBlock;
1497           }
1498 
1499           if (!useLock && cacheBlock && cacheConf.shouldLockOnCacheMiss(expectedBlockType)) {
1500             // check cache again with lock
1501             useLock = true;
1502             continue;
1503           }
1504           // Carry on, please load.
1505         }
1506 
1507         if (Trace.isTracing()) {
1508           traceScope.getSpan().addTimelineAnnotation("blockCacheMiss");
1509         }
1510         // Load block from filesystem.
1511         HFileBlock hfileBlock = fsBlockReader.readBlockData(dataBlockOffset, onDiskBlockSize, -1,
1512             pread);
1513         validateBlockType(hfileBlock, expectedBlockType);
1514         HFileBlock unpacked = hfileBlock.unpack(hfileContext, fsBlockReader);
1515         BlockType.BlockCategory category = hfileBlock.getBlockType().getCategory();
1516 
1517         // Cache the block if necessary
1518         if (cacheBlock && cacheConf.shouldCacheBlockOnRead(category)) {
1519           cacheConf.getBlockCache().cacheBlock(cacheKey,
1520             cacheConf.shouldCacheCompressed(category) ? hfileBlock : unpacked,
1521             cacheConf.isInMemory(), this.cacheConf.isCacheDataInL1());
1522         }
1523 
1524         if (updateCacheMetrics && hfileBlock.getBlockType().isData()) {
1525           HFile.dataBlockReadCnt.incrementAndGet();
1526         }
1527 
1528         return unpacked;
1529       }
1530     } finally {
1531       traceScope.close();
1532       if (lockEntry != null) {
1533         offsetLock.releaseLockEntry(lockEntry);
1534       }
1535     }
1536   }
1537 
1538   @Override
1539   public boolean hasMVCCInfo() {
1540     return includesMemstoreTS && decodeMemstoreTS;
1541   }
1542 
1543   /**
1544    * Compares the actual type of a block retrieved from cache or disk with its
1545    * expected type and throws an exception in case of a mismatch. Expected
1546    * block type of {@link BlockType#DATA} is considered to match the actual
1547    * block type [@link {@link BlockType#ENCODED_DATA} as well.
1548    * @param block a block retrieved from cache or disk
1549    * @param expectedBlockType the expected block type, or null to skip the
1550    *          check
1551    */
1552   private void validateBlockType(HFileBlock block,
1553       BlockType expectedBlockType) throws IOException {
1554     if (expectedBlockType == null) {
1555       return;
1556     }
1557     BlockType actualBlockType = block.getBlockType();
1558     if (expectedBlockType.isData() && actualBlockType.isData()) {
1559       // We consider DATA to match ENCODED_DATA for the purpose of this
1560       // verification.
1561       return;
1562     }
1563     if (actualBlockType != expectedBlockType) {
1564       throw new IOException("Expected block type " + expectedBlockType + ", " +
1565           "but got " + actualBlockType + ": " + block);
1566     }
1567   }
1568 
1569   /**
1570    * @return Last key as cell in the file. May be null if file has no entries. Note that
1571    *         this is not the last row key, but it is the Cell representation of the last
1572    *         key
1573    */
1574   @Override
1575   public Cell getLastKey() {
1576     return dataBlockIndexReader.isEmpty() ? null : lastKeyCell;
1577   }
1578 
1579   /**
1580    * @return Midkey for this file. We work with block boundaries only so
1581    *         returned midkey is an approximation only.
1582    * @throws IOException
1583    */
1584   @Override
1585   public Cell midkey() throws IOException {
1586     return dataBlockIndexReader.midkey();
1587   }
1588 
1589   @Override
1590   public void close() throws IOException {
1591     close(cacheConf.shouldEvictOnClose());
1592   }
1593 
1594   public void close(boolean evictOnClose) throws IOException {
1595     PrefetchExecutor.cancel(path);
1596     if (evictOnClose && cacheConf.isBlockCacheEnabled()) {
1597       int numEvicted = cacheConf.getBlockCache().evictBlocksByHfileName(name);
1598       if (LOG.isTraceEnabled()) {
1599         LOG.trace("On close, file=" + name + " evicted=" + numEvicted
1600           + " block(s)");
1601       }
1602     }
1603     fsBlockReader.closeStreams();
1604   }
1605 
1606   public DataBlockEncoding getEffectiveEncodingInCache(boolean isCompaction) {
1607     return dataBlockEncoder.getEffectiveEncodingInCache(isCompaction);
1608   }
1609 
1610   /** For testing */
1611   public HFileBlock.FSReader getUncachedBlockReader() {
1612     return fsBlockReader;
1613   }
1614 
1615   /**
1616    * Scanner that operates on encoded data blocks.
1617    */
1618   protected static class EncodedScanner extends HFileScannerImpl {
1619     private final HFileBlockDecodingContext decodingCtx;
1620     private final DataBlockEncoder.EncodedSeeker seeker;
1621     private final DataBlockEncoder dataBlockEncoder;
1622 
1623     public EncodedScanner(HFile.Reader reader, boolean cacheBlocks,
1624         boolean pread, boolean isCompaction, HFileContext meta) {
1625       super(reader, cacheBlocks, pread, isCompaction);
1626       DataBlockEncoding encoding = reader.getDataBlockEncoding();
1627       dataBlockEncoder = encoding.getEncoder();
1628       decodingCtx = dataBlockEncoder.newDataBlockDecodingContext(meta);
1629       seeker = dataBlockEncoder.createSeeker(
1630         reader.getComparator(), decodingCtx);
1631     }
1632 
1633     @Override
1634     public boolean isSeeked(){
1635       return curBlock != null;
1636     }
1637 
1638     public void setNonSeekedState() {
1639       reset();
1640     }
1641 
1642     /**
1643      * Updates the current block to be the given {@link HFileBlock}. Seeks to
1644      * the the first key/value pair.
1645      *
1646      * @param newBlock the block to make current
1647      * @throws CorruptHFileException
1648      */
1649     @Override
1650     protected void updateCurrentBlock(HFileBlock newBlock) throws CorruptHFileException {
1651 
1652       // sanity checks
1653       if (newBlock.getBlockType() != BlockType.ENCODED_DATA) {
1654         throw new IllegalStateException("EncodedScanner works only on encoded data blocks");
1655       }
1656       short dataBlockEncoderId = newBlock.getDataBlockEncodingId();
1657       if (!DataBlockEncoding.isCorrectEncoder(dataBlockEncoder, dataBlockEncoderId)) {
1658         String encoderCls = dataBlockEncoder.getClass().getName();
1659         throw new CorruptHFileException("Encoder " + encoderCls
1660           + " doesn't support data block encoding "
1661           + DataBlockEncoding.getNameFromId(dataBlockEncoderId));
1662       }
1663       updateCurrBlockRef(newBlock);
1664       ByteBuff encodedBuffer = getEncodedBuffer(newBlock);
1665       seeker.setCurrentBuffer(encodedBuffer);
1666       blockFetches++;
1667 
1668       // Reset the next indexed key
1669       this.nextIndexedKey = null;
1670     }
1671 
1672     private ByteBuff getEncodedBuffer(HFileBlock newBlock) {
1673       ByteBuff origBlock = newBlock.getBufferReadOnly();
1674       int pos = newBlock.headerSize() + DataBlockEncoding.ID_SIZE;
1675       origBlock.position(pos);
1676       origBlock
1677           .limit(pos + newBlock.getUncompressedSizeWithoutHeader() - DataBlockEncoding.ID_SIZE);
1678       return origBlock.slice();
1679     }
1680 
1681     @Override
1682     protected boolean processFirstDataBlock() throws IOException {
1683       seeker.rewind();
1684       return true;
1685     }
1686 
1687     @Override
1688     public boolean next() throws IOException {
1689       boolean isValid = seeker.next();
1690       if (!isValid) {
1691         HFileBlock newBlock = readNextDataBlock();
1692         isValid = newBlock != null;
1693         if (isValid) {
1694           updateCurrentBlock(newBlock);
1695         } else {
1696           setNonSeekedState();
1697         }
1698       }
1699       return isValid;
1700     }
1701 
1702     @Override
1703     public Cell getKey() {
1704       assertValidSeek();
1705       return seeker.getKey();
1706     }
1707 
1708     @Override
1709     public ByteBuffer getValue() {
1710       assertValidSeek();
1711       return seeker.getValueShallowCopy();
1712     }
1713 
1714     @Override
1715     public Cell getCell() {
1716       if (this.curBlock == null) {
1717         return null;
1718       }
1719       return seeker.getCell();
1720     }
1721 
1722     @Override
1723     public String getKeyString() {
1724       return CellUtil.toString(getKey(), true);
1725     }
1726 
1727     @Override
1728     public String getValueString() {
1729       ByteBuffer valueBuffer = getValue();
1730       return ByteBufferUtils.toStringBinary(valueBuffer);
1731     }
1732 
1733     private void assertValidSeek() {
1734       if (this.curBlock == null) {
1735         throw new NotSeekedException();
1736       }
1737     }
1738 
1739     protected Cell getFirstKeyCellInBlock(HFileBlock curBlock) {
1740       return dataBlockEncoder.getFirstKeyCellInBlock(getEncodedBuffer(curBlock));
1741     }
1742 
1743     @Override
1744     protected int loadBlockAndSeekToKey(HFileBlock seekToBlock, Cell nextIndexedKey,
1745         boolean rewind, Cell key, boolean seekBefore) throws IOException {
1746       if (this.curBlock == null
1747           || this.curBlock.getOffset() != seekToBlock.getOffset()) {
1748         updateCurrentBlock(seekToBlock);
1749       } else if (rewind) {
1750         seeker.rewind();
1751       }
1752       this.nextIndexedKey = nextIndexedKey;
1753       return seeker.seekToKeyInBlock(key, seekBefore);
1754     }
1755 
1756     public int compareKey(CellComparator comparator, Cell key) {
1757       return seeker.compareKey(comparator, key);
1758     }
1759   }
1760 
1761   /**
1762    * Returns a buffer with the Bloom filter metadata. The caller takes
1763    * ownership of the buffer.
1764    */
1765   @Override
1766   public DataInput getGeneralBloomFilterMetadata() throws IOException {
1767     return this.getBloomFilterMetadata(BlockType.GENERAL_BLOOM_META);
1768   }
1769 
1770   @Override
1771   public DataInput getDeleteBloomFilterMetadata() throws IOException {
1772     return this.getBloomFilterMetadata(BlockType.DELETE_FAMILY_BLOOM_META);
1773   }
1774 
1775   private DataInput getBloomFilterMetadata(BlockType blockType)
1776   throws IOException {
1777     if (blockType != BlockType.GENERAL_BLOOM_META &&
1778         blockType != BlockType.DELETE_FAMILY_BLOOM_META) {
1779       throw new RuntimeException("Block Type: " + blockType.toString() +
1780           " is not supported") ;
1781     }
1782 
1783     for (HFileBlock b : loadOnOpenBlocks)
1784       if (b.getBlockType() == blockType)
1785         return b.getByteStream();
1786     return null;
1787   }
1788 
1789   public boolean isFileInfoLoaded() {
1790     return true; // We load file info in constructor in version 2.
1791   }
1792 
1793   @Override
1794   public HFileContext getFileContext() {
1795     return hfileContext;
1796   }
1797 
1798   /**
1799    * Returns false if block prefetching was requested for this file and has
1800    * not completed, true otherwise
1801    */
1802   @VisibleForTesting
1803   public boolean prefetchComplete() {
1804     return PrefetchExecutor.isCompleted(path);
1805   }
1806 
1807   protected HFileContext createHFileContext(FSDataInputStreamWrapper fsdis, long fileSize,
1808       HFileSystem hfs, Path path, FixedFileTrailer trailer) throws IOException {
1809     HFileContextBuilder builder = new HFileContextBuilder()
1810       .withIncludesMvcc(shouldIncludeMemstoreTS())
1811       .withHBaseCheckSum(true)
1812       .withHFileName(this.getName())
1813       .withCompression(this.compressAlgo);
1814 
1815     // Check for any key material available
1816     byte[] keyBytes = trailer.getEncryptionKey();
1817     if (keyBytes != null) {
1818       Encryption.Context cryptoContext = Encryption.newContext(conf);
1819       Key key;
1820       String masterKeyName = conf.get(HConstants.CRYPTO_MASTERKEY_NAME_CONF_KEY,
1821         User.getCurrent().getShortName());
1822       try {
1823         // First try the master key
1824         key = EncryptionUtil.unwrapKey(conf, masterKeyName, keyBytes);
1825       } catch (KeyException e) {
1826         // If the current master key fails to unwrap, try the alternate, if
1827         // one is configured
1828         if (LOG.isDebugEnabled()) {
1829           LOG.debug("Unable to unwrap key with current master key '" + masterKeyName + "'");
1830         }
1831         String alternateKeyName =
1832           conf.get(HConstants.CRYPTO_MASTERKEY_ALTERNATE_NAME_CONF_KEY);
1833         if (alternateKeyName != null) {
1834           try {
1835             key = EncryptionUtil.unwrapKey(conf, alternateKeyName, keyBytes);
1836           } catch (KeyException ex) {
1837             throw new IOException(ex);
1838           }
1839         } else {
1840           throw new IOException(e);
1841         }
1842       }
1843       // Use the algorithm the key wants
1844       Cipher cipher = Encryption.getCipher(conf, key.getAlgorithm());
1845       if (cipher == null) {
1846         throw new IOException("Cipher '" + key.getAlgorithm() + "' is not available");
1847       }
1848       cryptoContext.setCipher(cipher);
1849       cryptoContext.setKey(key);
1850       builder.withEncryptionContext(cryptoContext);
1851     }
1852 
1853     HFileContext context = builder.build();
1854 
1855     if (LOG.isTraceEnabled()) {
1856       LOG.trace("Reader" + (path != null? " for " + path: "") +
1857         " initialized with cacheConf: " + cacheConf +
1858         " comparator: " + comparator.getClass().getSimpleName() +
1859         " fileContext: " + context);
1860     }
1861 
1862     return context;
1863   }
1864 
1865   /**
1866    * Create a Scanner on this file. No seeks or reads are done on creation. Call
1867    * {@link HFileScanner#seekTo(Cell)} to position an start the read. There is
1868    * nothing to clean up in a Scanner. Letting go of your references to the
1869    * scanner is sufficient. NOTE: Do not use this overload of getScanner for
1870    * compactions. See {@link #getScanner(boolean, boolean, boolean)}
1871    *
1872    * @param cacheBlocks True if we should cache blocks read in by this scanner.
1873    * @param pread Use positional read rather than seek+read if true (pread is
1874    *          better for random reads, seek+read is better scanning).
1875    * @return Scanner on this file.
1876    */
1877   @Override
1878   public HFileScanner getScanner(boolean cacheBlocks, final boolean pread) {
1879     return getScanner(cacheBlocks, pread, false);
1880   }
1881 
1882   /**
1883    * Create a Scanner on this file. No seeks or reads are done on creation. Call
1884    * {@link HFileScanner#seekTo(Cell)} to position an start the read. There is
1885    * nothing to clean up in a Scanner. Letting go of your references to the
1886    * scanner is sufficient.
1887    * @param cacheBlocks
1888    *          True if we should cache blocks read in by this scanner.
1889    * @param pread
1890    *          Use positional read rather than seek+read if true (pread is better
1891    *          for random reads, seek+read is better scanning).
1892    * @param isCompaction
1893    *          is scanner being used for a compaction?
1894    * @return Scanner on this file.
1895    */
1896   @Override
1897   public HFileScanner getScanner(boolean cacheBlocks, final boolean pread,
1898       final boolean isCompaction) {
1899     if (dataBlockEncoder.useEncodedScanner()) {
1900       return new EncodedScanner(this, cacheBlocks, pread, isCompaction, this.hfileContext);
1901     }
1902     return new HFileScannerImpl(this, cacheBlocks, pread, isCompaction);
1903   }
1904 
1905   public int getMajorVersion() {
1906     return 3;
1907   }
1908 }