1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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
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
73
74
75
76 private static final Log LOG = LogFactory.getLog(HFileReaderImpl.class);
77
78
79 private HFileBlockIndex.CellBasedKeyBlockIndexReader dataBlockIndexReader;
80
81
82 private HFileBlockIndex.ByteArrayKeyBlockIndexReader metaBlockIndexReader;
83
84 private final FixedFileTrailer trailer;
85
86
87 private final Compression.Algorithm compressAlgo;
88
89 private boolean isPrimaryReplicaReader;
90
91
92
93
94
95 private HFileDataBlockEncoder dataBlockEncoder = NoOpDataBlockEncoder.INSTANCE;
96
97
98 private Cell lastKeyCell = null;
99
100
101 private int avgKeyLen = -1;
102
103
104 private int avgValueLen = -1;
105
106
107 private CellComparator comparator = CellComparator.COMPARATOR;
108
109
110 private final long fileSize;
111
112
113 private final CacheConfig cacheConf;
114
115
116 private final Path path;
117
118
119 private final String name;
120
121 private FileInfo fileInfo;
122
123 private Configuration conf;
124
125 private HFileContext hfileContext;
126
127
128 private HFileBlock.FSReader fsBlockReader;
129
130
131
132
133
134
135
136 private IdLock offsetLock = new IdLock();
137
138
139
140
141
142 private List<HFileBlock> loadOnOpenBlocks = new ArrayList<HFileBlock>();
143
144
145 static final int MIN_MINOR_VERSION = 0;
146
147
148
149
150 static final int MAX_MINOR_VERSION = 3;
151
152
153
154
155 private static final int MIN_V2_MINOR_VERSION_WITH_PB = 3;
156
157
158 static final int MINOR_VERSION_WITH_FAKED_KEY = 3;
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
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
196 comparator = trailer.createComparator();
197 dataBlockIndexReader = new HFileBlockIndex.CellBasedKeyBlockIndexReader(comparator,
198 trailer.getNumDataIndexLevels(), this);
199 metaBlockIndexReader = new HFileBlockIndex.ByteArrayKeyBlockIndexReader(1);
200
201
202
203 HFileBlock.BlockIterator blockIter = fsBlockReader.blockRange(
204 trailer.getLoadOnOpenDataOffset(),
205 fileSize - trailer.getTrailerSize());
206
207
208
209 dataBlockIndexReader.readMultiLevelIndexRoot(
210 blockIter.nextBlockWithBlockType(BlockType.ROOT_INDEX),
211 trailer.getDataIndexCount());
212
213
214 metaBlockIndexReader.readRootIndex(
215 blockIter.nextBlockWithBlockType(BlockType.ROOT_INDEX),
216 trailer.getMetaIndexCount());
217
218
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
238 dataBlockEncoder = HFileDataBlockEncoderImpl.createFromFileInfo(fileInfo);
239 fsBlockReader.setDataBlockEncoder(dataBlockEncoder);
240
241
242 HFileBlock b;
243 while ((b = blockIter.nextBlock()) != null) {
244 loadOnOpenBlocks.add(b);
245 }
246
247
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
266
267
268
269 returnBlock(block);
270 prevBlock = block;
271 offset += block.getOnDiskSizeWithHeader();
272 }
273 } catch (IOException e) {
274
275 if (LOG.isTraceEnabled()) {
276 LOG.trace("Exception encountered while prefetching " + path + ":", e);
277 }
278 } catch (Exception e) {
279
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
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
301
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
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
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
362
363
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
376
377
378
379
380 @Override
381 public byte[] getFirstRowKey() {
382 Cell firstKey = getFirstKey();
383
384 return firstKey == null? null: CellUtil.cloneRow(firstKey);
385 }
386
387
388
389
390
391
392
393 @Override
394 public byte[] getLastRowKey() {
395 Cell lastKey = getLastKey();
396 return lastKey == null? null: CellUtil.cloneRow(lastKey);
397 }
398
399
400 @Override
401 public long getEntries() {
402 return trailer.getEntryCount();
403 }
404
405
406 @Override
407 public CellComparator getComparator() {
408 return comparator;
409 }
410
411
412 @Override
413 public Compression.Algorithm getCompressionAlgorithm() {
414 return compressAlgo;
415 }
416
417
418
419
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
459
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
478 protected volatile int blockFetches;
479 protected final HFile.Reader reader;
480 private int currTagsLen;
481
482 private ByteBufferedKeyOnlyKeyValue bufBackedKeyOnlyKv = new ByteBufferedKeyOnlyKeyValue();
483
484 final ObjectIntPair<ByteBuffer> pair = new ObjectIntPair<ByteBuffer>();
485
486
487
488
489
490
491
492
493 protected Cell nextIndexedKey;
494
495 protected HFileBlock curBlock;
496
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
513 if (this.curBlock != null && this.curBlock.usesSharedMemory()) {
514 prevBlocks.add(this.curBlock);
515 }
516 this.curBlock = block;
517 }
518
519 void reset() {
520
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
588
589
590
591
592
593
594
595
596 long ll = blockBuffer.getLongAfterPosition(0);
597
598 this.currKeyLen = (int)(ll >> Integer.SIZE);
599 this.currValueLen = (int)(Bytes.MASK_FOR_LOWER_INT_IN_LONG ^ ll);
600 checkKeyValueLen();
601
602 int p = (Bytes.SIZEOF_LONG + currKeyLen + currValueLen);
603 if (reader.getFileContext().isIncludesTags()) {
604
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
623
624
625 protected void readMvccVersion(final int offsetFromPos) {
626
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
638
639
640 private void _readMvccVersion(int offsetFromPos) {
641
642
643
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
676
677
678
679
680
681
682
683
684
685
686
687
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
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
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
721 offsetFromPos += tlen + (Bytes.SIZEOF_SHORT);
722 }
723 if (this.reader.shouldIncludeMemstoreTS()) {
724
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;
738 }
739 currKeyLen = klen;
740 currValueLen = vlen;
741 currTagsLen = tlen;
742 return 0;
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
754 lastKeyValueSize = klen + vlen + currMemstoreTSLen + KEY_VALUE_LEN_SIZE;
755
756 if (reader.getFileContext().isIncludesTags()) {
757 lastKeyValueSize += tlen + Bytes.SIZEOF_SHORT;
758 }
759 blockBuffer.skip(lastKeyValueSize);
760 } while (blockBuffer.hasRemaining());
761
762
763
764
765 blockBuffer.moveBack(lastKeyValueSize);
766 readKeyValueLen();
767 return 1;
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
787
788 return compared;
789 } else {
790
791 if (this.nextIndexedKey != null &&
792 (this.nextIndexedKey == HConstants.NO_NEXT_INDEXED_KEY || reader
793 .getComparator().compareKeyIgnoresMvcc(key, nextIndexedKey) < 0)) {
794
795
796
797
798
799
800 return loadBlockAndSeekToKey(this.curBlock, nextIndexedKey, false, key,
801 false);
802 }
803
804 }
805 }
806
807
808 return seekTo(key, false);
809 }
810
811
812
813
814
815
816
817
818
819
820
821
822
823
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
831
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
850 if (previousBlockOffset == -1) {
851
852 return false;
853 }
854
855
856
857
858 reader.returnBlock(seekToBlock);
859
860
861
862
863
864 int prevBlockSize = -1;
865 seekToBlock = reader.readBlock(previousBlockOffset,
866 prevBlockSize, cacheBlocks,
867 pread, isCompaction, true, BlockType.DATA, getEffectiveDataBlockEncoding());
868
869
870 }
871 loadBlockAndSeekToKey(seekToBlock, firstKey, true, key, true);
872 return true;
873 }
874
875
876
877
878
879
880
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
898
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
905
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
930
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
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
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
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
1077
1078
1079
1080 private boolean positionForNextBlock() throws IOException {
1081
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
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
1104 if (blockBuffer.remaining() <= 0) {
1105 return positionForNextBlock();
1106 }
1107
1108
1109 readKeyValueLen();
1110 return true;
1111 }
1112
1113
1114
1115
1116
1117
1118
1119
1120 @Override
1121 public boolean next() throws IOException {
1122
1123
1124 assertSeeked();
1125 positionThisBlockBuffer();
1126 return _next();
1127 }
1128
1129
1130
1131
1132
1133
1134
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
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
1183 this.nextIndexedKey = nextIndexedKey;
1184 return blockSeek(key, seekBefore);
1185 }
1186
1187
1188
1189
1190
1191 protected final boolean checkLen(final int v) {
1192 return v < 0 || v > this.blockBuffer.limit();
1193 }
1194
1195
1196
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
1210
1211
1212
1213
1214 protected void updateCurrentBlock(HFileBlock newBlock) throws IOException {
1215
1216
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
1230 this.nextIndexedKey = null;
1231 }
1232
1233 protected Cell getFirstKeyCellInBlock(HFileBlock curBlock) {
1234 ByteBuff buffer = curBlock.getBufferWithoutHeader();
1235
1236 buffer.rewind();
1237 int klen = buffer.getInt();
1238 buffer.skip(Bytes.SIZEOF_INT);
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
1290 public static final int MINOR_VERSION_WITH_CHECKSUM = 1;
1291
1292 public static final int MINOR_VERSION_NO_CHECKSUM = 0;
1293
1294
1295 public static final int PBUF_TRAILER_MINOR_VERSION = 2;
1296
1297
1298
1299
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
1317
1318
1319 private HFileBlock getCachedBlock(BlockCacheKey cacheKey, boolean cacheBlock, boolean useLock,
1320 boolean isCompaction, boolean updateCacheMetrics, BlockType expectedBlockType,
1321 DataBlockEncoding expectedDataBlockEncoding) throws IOException {
1322
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
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
1344
1345
1346 if (cachedBlock.getBlockType().isData() &&
1347 !actualDataBlockEncoding.equals(expectedDataBlockEncoding)) {
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358 if (!expectedDataBlockEncoding.equals(DataBlockEncoding.NONE) &&
1359 !actualDataBlockEncoding.equals(DataBlockEncoding.NONE)) {
1360
1361
1362
1363
1364
1365
1366 LOG.info("Evicting cached block with key " + cacheKey +
1367 " because of a data block encoding mismatch" +
1368 "; expected: " + expectedDataBlockEncoding +
1369 ", actual: " + actualDataBlockEncoding);
1370
1371
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
1385
1386
1387
1388
1389 @Override
1390 public HFileBlock getMetaBlock(String metaBlockName, boolean cacheBlock)
1391 throws IOException {
1392 if (trailer.getMetaIndexCount() == 0) {
1393 return null;
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
1407
1408
1409 synchronized (metaBlockIndexReader
1410 .getRootBlockKey(block)) {
1411
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
1423
1424 return cachedBlock;
1425 }
1426
1427 }
1428
1429 HFileBlock metaBlock = fsBlockReader.readBlockData(metaBlockOffset,
1430 blockSize, -1, true).unpack(hfileContext, fsBlockReader);
1431
1432
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
1456
1457
1458
1459
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
1470 if (cacheConf.shouldReadBlockFromCache(expectedBlockType)) {
1471 if (useLock) {
1472 lockEntry = offsetLock.getLockEntry(dataBlockOffset);
1473 }
1474
1475
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
1488
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
1496 return cachedBlock;
1497 }
1498
1499 if (!useLock && cacheBlock && cacheConf.shouldLockOnCacheMiss(expectedBlockType)) {
1500
1501 useLock = true;
1502 continue;
1503 }
1504
1505 }
1506
1507 if (Trace.isTracing()) {
1508 traceScope.getSpan().addTimelineAnnotation("blockCacheMiss");
1509 }
1510
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
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
1545
1546
1547
1548
1549
1550
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
1560
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
1571
1572
1573
1574 @Override
1575 public Cell getLastKey() {
1576 return dataBlockIndexReader.isEmpty() ? null : lastKeyCell;
1577 }
1578
1579
1580
1581
1582
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
1611 public HFileBlock.FSReader getUncachedBlockReader() {
1612 return fsBlockReader;
1613 }
1614
1615
1616
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
1644
1645
1646
1647
1648
1649 @Override
1650 protected void updateCurrentBlock(HFileBlock newBlock) throws CorruptHFileException {
1651
1652
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
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
1763
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;
1791 }
1792
1793 @Override
1794 public HFileContext getFileContext() {
1795 return hfileContext;
1796 }
1797
1798
1799
1800
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
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
1824 key = EncryptionUtil.unwrapKey(conf, masterKeyName, keyBytes);
1825 } catch (KeyException e) {
1826
1827
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
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
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877 @Override
1878 public HFileScanner getScanner(boolean cacheBlocks, final boolean pread) {
1879 return getScanner(cacheBlocks, pread, false);
1880 }
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
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 }