1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.hadoop.hbase.io.hfile;
21
22 import java.io.ByteArrayInputStream;
23 import java.io.DataInput;
24 import java.io.DataInputStream;
25 import java.io.IOException;
26 import java.nio.ByteBuffer;
27
28 import org.apache.commons.logging.Log;
29 import org.apache.commons.logging.LogFactory;
30 import org.apache.hadoop.fs.FSDataInputStream;
31 import org.apache.hadoop.fs.Path;
32 import org.apache.hadoop.hbase.KeyValue;
33 import org.apache.hadoop.hbase.io.encoding.DataBlockEncoding;
34 import org.apache.hadoop.hbase.io.hfile.BlockType.BlockCategory;
35 import org.apache.hadoop.hbase.io.hfile.HFile.FileInfo;
36 import org.apache.hadoop.hbase.io.hfile.HFile.Writer;
37 import org.apache.hadoop.hbase.regionserver.metrics.SchemaMetrics;
38 import org.apache.hadoop.hbase.util.Bytes;
39 import org.apache.hadoop.io.IOUtils;
40 import org.apache.hadoop.io.RawComparator;
41
42 import com.google.common.base.Preconditions;
43
44
45
46
47
48
49 public class HFileReaderV1 extends AbstractHFileReader {
50 private static final Log LOG = LogFactory.getLog(HFileReaderV1.class);
51
52 private volatile boolean fileInfoLoaded = false;
53
54
55
56
57
58
59
60
61
62
63 public HFileReaderV1(Path path, FixedFileTrailer trailer,
64 final FSDataInputStream fsdis, final long size,
65 final boolean closeIStream,
66 final CacheConfig cacheConf) throws IOException {
67 super(path, trailer, fsdis, size, closeIStream, cacheConf);
68
69 trailer.expectMajorVersion(1);
70 fsBlockReader = new HFileBlock.FSReaderV1(fsdis, compressAlgo, fileSize);
71 }
72
73 private byte[] readAllIndex(final FSDataInputStream in,
74 final long indexOffset, final int indexSize) throws IOException {
75 byte[] allIndex = new byte[indexSize];
76 in.seek(indexOffset);
77 IOUtils.readFully(in, allIndex, 0, allIndex.length);
78
79 return allIndex;
80 }
81
82
83
84
85
86
87
88
89 @Override
90 public FileInfo loadFileInfo() throws IOException {
91 if (fileInfoLoaded)
92 return fileInfo;
93
94
95 istream.seek(trailer.getFileInfoOffset());
96 fileInfo = new FileInfo();
97 fileInfo.readFields(istream);
98 lastKey = fileInfo.get(FileInfo.LASTKEY);
99 avgKeyLen = Bytes.toInt(fileInfo.get(FileInfo.AVG_KEY_LEN));
100 avgValueLen = Bytes.toInt(fileInfo.get(FileInfo.AVG_VALUE_LEN));
101
102
103 String clazzName = Bytes.toString(fileInfo.get(FileInfo.COMPARATOR));
104 comparator = getComparator(clazzName);
105
106 dataBlockIndexReader =
107 new HFileBlockIndex.BlockIndexReader(comparator, 1);
108 metaBlockIndexReader =
109 new HFileBlockIndex.BlockIndexReader(Bytes.BYTES_RAWCOMPARATOR, 1);
110
111 int sizeToLoadOnOpen = (int) (fileSize - trailer.getLoadOnOpenDataOffset() -
112 trailer.getTrailerSize());
113 byte[] dataAndMetaIndex = readAllIndex(istream,
114 trailer.getLoadOnOpenDataOffset(), sizeToLoadOnOpen);
115
116 ByteArrayInputStream bis = new ByteArrayInputStream(dataAndMetaIndex);
117 DataInputStream dis = new DataInputStream(bis);
118
119
120 if (trailer.getDataIndexCount() > 0)
121 BlockType.INDEX_V1.readAndCheck(dis);
122 dataBlockIndexReader.readRootIndex(dis, trailer.getDataIndexCount());
123
124
125 if (trailer.getMetaIndexCount() > 0)
126 BlockType.INDEX_V1.readAndCheck(dis);
127 metaBlockIndexReader.readRootIndex(dis, trailer.getMetaIndexCount());
128
129 fileInfoLoaded = true;
130 return fileInfo;
131 }
132
133
134
135
136
137
138
139
140 @SuppressWarnings("unchecked")
141 private RawComparator<byte[]> getComparator(final String clazzName)
142 throws IOException {
143 if (clazzName == null || clazzName.length() == 0) {
144 return null;
145 }
146 try {
147 return (RawComparator<byte[]>)Class.forName(clazzName).newInstance();
148 } catch (InstantiationException e) {
149 throw new IOException(e);
150 } catch (IllegalAccessException e) {
151 throw new IOException(e);
152 } catch (ClassNotFoundException e) {
153 throw new IOException(e);
154 }
155 }
156
157
158
159
160
161
162
163
164
165
166
167
168
169 @Override
170 public HFileScanner getScanner(boolean cacheBlocks, final boolean pread,
171 final boolean isCompaction) {
172 return new ScannerV1(this, cacheBlocks, pread, isCompaction);
173 }
174
175
176
177
178
179
180 protected int blockContainingKey(final byte[] key, int offset, int length) {
181 Preconditions.checkState(!dataBlockIndexReader.isEmpty(),
182 "Block index not loaded");
183 return dataBlockIndexReader.rootBlockContainingKey(key, offset, length);
184 }
185
186
187
188
189
190
191
192 @Override
193 public ByteBuffer getMetaBlock(String metaBlockName, boolean cacheBlock)
194 throws IOException {
195 if (trailer.getMetaIndexCount() == 0) {
196 return null;
197 }
198 if (metaBlockIndexReader == null) {
199 throw new IOException("Meta index not loaded");
200 }
201
202 byte[] nameBytes = Bytes.toBytes(metaBlockName);
203 int block = metaBlockIndexReader.rootBlockContainingKey(nameBytes, 0,
204 nameBytes.length);
205 if (block == -1)
206 return null;
207 long offset = metaBlockIndexReader.getRootBlockOffset(block);
208 long nextOffset;
209 if (block == metaBlockIndexReader.getRootBlockCount() - 1) {
210 nextOffset = trailer.getFileInfoOffset();
211 } else {
212 nextOffset = metaBlockIndexReader.getRootBlockOffset(block + 1);
213 }
214
215 long startTimeNs = System.nanoTime();
216
217 BlockCacheKey cacheKey = new BlockCacheKey(name, offset,
218 DataBlockEncoding.NONE, BlockType.META);
219
220 BlockCategory effectiveCategory = BlockCategory.META;
221 if (metaBlockName.equals(HFileWriterV1.BLOOM_FILTER_META_KEY) ||
222 metaBlockName.equals(HFileWriterV1.BLOOM_FILTER_DATA_KEY)) {
223 effectiveCategory = BlockCategory.BLOOM;
224 }
225
226
227 synchronized (metaBlockIndexReader.getRootBlockKey(block)) {
228
229 if (cacheConf.isBlockCacheEnabled()) {
230 HFileBlock cachedBlock =
231 (HFileBlock) cacheConf.getBlockCache().getBlock(cacheKey,
232 cacheConf.shouldCacheBlockOnRead(effectiveCategory), false);
233 if (cachedBlock != null) {
234 getSchemaMetrics().updateOnCacheHit(effectiveCategory,
235 SchemaMetrics.NO_COMPACTION);
236 return cachedBlock.getBufferWithoutHeader();
237 }
238
239 }
240
241 HFileBlock hfileBlock = fsBlockReader.readBlockData(offset,
242 nextOffset - offset, metaBlockIndexReader.getRootBlockDataSize(block),
243 true);
244 passSchemaMetricsTo(hfileBlock);
245 hfileBlock.expectType(BlockType.META);
246
247 final long delta = System.nanoTime() - startTimeNs;
248 HFile.offerReadLatency(delta, true);
249 getSchemaMetrics().updateOnCacheMiss(effectiveCategory,
250 SchemaMetrics.NO_COMPACTION, delta);
251
252
253 if (cacheBlock && cacheConf.shouldCacheBlockOnRead(effectiveCategory)) {
254 cacheConf.getBlockCache().cacheBlock(cacheKey, hfileBlock,
255 cacheConf.isInMemory());
256 }
257
258 return hfileBlock.getBufferWithoutHeader();
259 }
260 }
261
262
263
264
265
266
267
268
269
270
271 ByteBuffer readBlockBuffer(int block, boolean cacheBlock,
272 final boolean pread, final boolean isCompaction) throws IOException {
273 if (dataBlockIndexReader == null) {
274 throw new IOException("Block index not loaded");
275 }
276 if (block < 0 || block >= dataBlockIndexReader.getRootBlockCount()) {
277 throw new IOException("Requested block is out of range: " + block +
278 ", max: " + dataBlockIndexReader.getRootBlockCount());
279 }
280
281 long offset = dataBlockIndexReader.getRootBlockOffset(block);
282 BlockCacheKey cacheKey = new BlockCacheKey(name, offset);
283
284
285
286
287
288
289 synchronized (dataBlockIndexReader.getRootBlockKey(block)) {
290
291 if (cacheConf.isBlockCacheEnabled()) {
292 HFileBlock cachedBlock =
293 (HFileBlock) cacheConf.getBlockCache().getBlock(cacheKey,
294 cacheConf.shouldCacheDataOnRead(), false);
295 if (cachedBlock != null) {
296 getSchemaMetrics().updateOnCacheHit(
297 cachedBlock.getBlockType().getCategory(), isCompaction);
298 return cachedBlock.getBufferWithoutHeader();
299 }
300
301 }
302
303
304 long startTimeNs = System.nanoTime();
305 long nextOffset;
306
307 if (block == dataBlockIndexReader.getRootBlockCount() - 1) {
308
309
310 nextOffset = (metaBlockIndexReader.getRootBlockCount() == 0) ?
311 this.trailer.getFileInfoOffset() :
312 metaBlockIndexReader.getRootBlockOffset(0);
313 } else {
314 nextOffset = dataBlockIndexReader.getRootBlockOffset(block + 1);
315 }
316
317 HFileBlock hfileBlock = fsBlockReader.readBlockData(offset, nextOffset
318 - offset, dataBlockIndexReader.getRootBlockDataSize(block), pread);
319 passSchemaMetricsTo(hfileBlock);
320 hfileBlock.expectType(BlockType.DATA);
321
322 final long delta = System.nanoTime() - startTimeNs;
323 HFile.offerReadLatency(delta, pread);
324 getSchemaMetrics().updateOnCacheMiss(BlockCategory.DATA, isCompaction,
325 delta);
326
327
328 if (cacheBlock && cacheConf.shouldCacheBlockOnRead(
329 hfileBlock.getBlockType().getCategory())) {
330 cacheConf.getBlockCache().cacheBlock(cacheKey, hfileBlock,
331 cacheConf.isInMemory());
332 }
333 return hfileBlock.getBufferWithoutHeader();
334 }
335 }
336
337
338
339
340
341
342 public byte[] getLastKey() {
343 if (!fileInfoLoaded) {
344 throw new RuntimeException("Load file info first");
345 }
346 return dataBlockIndexReader.isEmpty() ? null : lastKey;
347 }
348
349
350
351
352
353
354
355 @Override
356 public byte[] midkey() throws IOException {
357 Preconditions.checkState(isFileInfoLoaded(), "File info is not loaded");
358 Preconditions.checkState(!dataBlockIndexReader.isEmpty(),
359 "Data block index is not loaded or is empty");
360 return dataBlockIndexReader.midkey();
361 }
362
363 @Override
364 public void close() throws IOException {
365 close(cacheConf.shouldEvictOnClose());
366 }
367
368 @Override
369 public void close(boolean evictOnClose) throws IOException {
370 if (evictOnClose && cacheConf.isBlockCacheEnabled()) {
371 int numEvicted = 0;
372 for (int i = 0; i < dataBlockIndexReader.getRootBlockCount(); i++) {
373 if (cacheConf.getBlockCache().evictBlock(
374 new BlockCacheKey(name,
375 dataBlockIndexReader.getRootBlockOffset(i),
376 DataBlockEncoding.NONE, BlockType.DATA))) {
377 numEvicted++;
378 }
379 }
380 LOG.debug("On close of file " + name + " evicted " + numEvicted
381 + " block(s) of " + dataBlockIndexReader.getRootBlockCount()
382 + " total blocks");
383 }
384 if (this.closeIStream && this.istream != null) {
385 this.istream.close();
386 this.istream = null;
387 }
388
389 getSchemaMetrics().flushMetrics();
390 }
391
392 protected abstract static class AbstractScannerV1
393 extends AbstractHFileReader.Scanner {
394 protected int currBlock;
395
396
397
398
399
400 protected HFileReaderV1 reader;
401
402 public AbstractScannerV1(HFileReaderV1 reader, boolean cacheBlocks,
403 final boolean pread, final boolean isCompaction) {
404 super(reader, cacheBlocks, pread, isCompaction);
405 this.reader = (HFileReaderV1) reader;
406 }
407
408
409
410
411
412
413
414
415
416
417
418 protected abstract int blockSeek(byte[] key, int offset, int length,
419 boolean seekBefore);
420
421 protected abstract void loadBlock(int bloc, boolean rewind)
422 throws IOException;
423
424 @Override
425 public int seekTo(byte[] key, int offset, int length) throws IOException {
426 int b = reader.blockContainingKey(key, offset, length);
427 if (b < 0) return -1;
428
429 loadBlock(b, true);
430 return blockSeek(key, offset, length, false);
431 }
432
433 @Override
434 public int reseekTo(byte[] key, int offset, int length)
435 throws IOException {
436 if (blockBuffer != null && currKeyLen != 0) {
437 ByteBuffer bb = getKey();
438 int compared = reader.getComparator().compare(key, offset,
439 length, bb.array(), bb.arrayOffset(), bb.limit());
440 if (compared < 1) {
441
442
443 return compared;
444 }
445 }
446
447 int b = reader.blockContainingKey(key, offset, length);
448 if (b < 0) {
449 return -1;
450 }
451 loadBlock(b, false);
452 return blockSeek(key, offset, length, false);
453 }
454
455 @Override
456 public boolean seekBefore(byte[] key, int offset, int length)
457 throws IOException {
458 int b = reader.blockContainingKey(key, offset, length);
459 if (b < 0)
460 return false;
461
462
463 byte[] firstkKey = reader.getDataBlockIndexReader().getRootBlockKey(b);
464 if (reader.getComparator().compare(firstkKey, 0, firstkKey.length,
465 key, offset, length) == 0) {
466
467
468 if (b == 0) {
469
470 return false;
471 }
472 b--;
473
474
475 }
476 loadBlock(b, true);
477 blockSeek(key, offset, length, true);
478 return true;
479 }
480 }
481
482
483
484
485
486 protected static class ScannerV1 extends AbstractScannerV1 {
487 private HFileReaderV1 reader;
488
489 public ScannerV1(HFileReaderV1 reader, boolean cacheBlocks,
490 final boolean pread, final boolean isCompaction) {
491 super(reader, cacheBlocks, pread, isCompaction);
492 this.reader = reader;
493 }
494
495 @Override
496 public KeyValue getKeyValue() {
497 if (blockBuffer == null) {
498 return null;
499 }
500 return new KeyValue(blockBuffer.array(), blockBuffer.arrayOffset()
501 + blockBuffer.position() - 8);
502 }
503
504 @Override
505 public ByteBuffer getKey() {
506 Preconditions.checkState(blockBuffer != null && currKeyLen > 0,
507 "you need to seekTo() before calling getKey()");
508
509 ByteBuffer keyBuff = blockBuffer.slice();
510 keyBuff.limit(currKeyLen);
511 keyBuff.rewind();
512
513 return keyBuff;
514 }
515
516 @Override
517 public ByteBuffer getValue() {
518 if (blockBuffer == null || currKeyLen == 0) {
519 throw new RuntimeException(
520 "you need to seekTo() before calling getValue()");
521 }
522
523
524 ByteBuffer valueBuff = blockBuffer.slice();
525 valueBuff.position(currKeyLen);
526 valueBuff = valueBuff.slice();
527 valueBuff.limit(currValueLen);
528 valueBuff.rewind();
529 return valueBuff;
530 }
531
532 @Override
533 public boolean next() throws IOException {
534 if (blockBuffer == null) {
535 throw new IOException("Next called on non-seeked scanner");
536 }
537
538 try {
539 blockBuffer.position(blockBuffer.position() + currKeyLen
540 + currValueLen);
541 } catch (IllegalArgumentException e) {
542 LOG.error("Current pos = " + blockBuffer.position() +
543 "; currKeyLen = " + currKeyLen +
544 "; currValLen = " + currValueLen +
545 "; block limit = " + blockBuffer.limit() +
546 "; HFile name = " + reader.getName() +
547 "; currBlock id = " + currBlock, e);
548 throw e;
549 }
550 if (blockBuffer.remaining() <= 0) {
551 currBlock++;
552 if (currBlock >= reader.getDataBlockIndexReader().getRootBlockCount()) {
553
554 currBlock = 0;
555 blockBuffer = null;
556 return false;
557 }
558 blockBuffer = reader.readBlockBuffer(currBlock, cacheBlocks, pread,
559 isCompaction);
560 currKeyLen = blockBuffer.getInt();
561 currValueLen = blockBuffer.getInt();
562 blockFetches++;
563 return true;
564 }
565
566 currKeyLen = blockBuffer.getInt();
567 currValueLen = blockBuffer.getInt();
568 return true;
569 }
570
571 @Override
572 protected int blockSeek(byte[] key, int offset, int length,
573 boolean seekBefore) {
574 int klen, vlen;
575 int lastLen = 0;
576 do {
577 klen = blockBuffer.getInt();
578 vlen = blockBuffer.getInt();
579 int comp = reader.getComparator().compare(key, offset, length,
580 blockBuffer.array(),
581 blockBuffer.arrayOffset() + blockBuffer.position(), klen);
582 if (comp == 0) {
583 if (seekBefore) {
584 blockBuffer.position(blockBuffer.position() - lastLen - 16);
585 currKeyLen = blockBuffer.getInt();
586 currValueLen = blockBuffer.getInt();
587 return 1;
588 }
589 currKeyLen = klen;
590 currValueLen = vlen;
591 return 0;
592 }
593 if (comp < 0) {
594
595 blockBuffer.position(blockBuffer.position() - lastLen - 16);
596 currKeyLen = blockBuffer.getInt();
597 currValueLen = blockBuffer.getInt();
598 return 1;
599 }
600 blockBuffer.position(blockBuffer.position() + klen + vlen);
601 lastLen = klen + vlen;
602 } while (blockBuffer.remaining() > 0);
603
604
605
606
607 blockBuffer.position(blockBuffer.position() - lastLen - 8);
608 currKeyLen = blockBuffer.getInt();
609 currValueLen = blockBuffer.getInt();
610 return 1;
611 }
612
613 @Override
614 public String getKeyString() {
615 return Bytes.toStringBinary(blockBuffer.array(),
616 blockBuffer.arrayOffset() + blockBuffer.position(), currKeyLen);
617 }
618
619 @Override
620 public String getValueString() {
621 return Bytes.toString(blockBuffer.array(), blockBuffer.arrayOffset() +
622 blockBuffer.position() + currKeyLen, currValueLen);
623 }
624
625 @Override
626 public boolean seekTo() throws IOException {
627 if (reader.getDataBlockIndexReader().isEmpty()) {
628 return false;
629 }
630 if (blockBuffer != null && currBlock == 0) {
631 blockBuffer.rewind();
632 currKeyLen = blockBuffer.getInt();
633 currValueLen = blockBuffer.getInt();
634 return true;
635 }
636 currBlock = 0;
637 blockBuffer = reader.readBlockBuffer(currBlock, cacheBlocks, pread,
638 isCompaction);
639 currKeyLen = blockBuffer.getInt();
640 currValueLen = blockBuffer.getInt();
641 blockFetches++;
642 return true;
643 }
644
645 @Override
646 protected void loadBlock(int bloc, boolean rewind) throws IOException {
647 if (blockBuffer == null) {
648 blockBuffer = reader.readBlockBuffer(bloc, cacheBlocks, pread,
649 isCompaction);
650 currBlock = bloc;
651 blockFetches++;
652 } else {
653 if (bloc != currBlock) {
654 blockBuffer = reader.readBlockBuffer(bloc, cacheBlocks, pread,
655 isCompaction);
656 currBlock = bloc;
657 blockFetches++;
658 } else {
659
660 if (rewind) {
661 blockBuffer.rewind();
662 }
663 else {
664
665 blockBuffer.position(blockBuffer.position()-8);
666 }
667 }
668 }
669 }
670
671 }
672
673 @Override
674 public HFileBlock readBlock(long offset, long onDiskBlockSize,
675 boolean cacheBlock, boolean pread, boolean isCompaction,
676 BlockType expectedBlockType) {
677 throw new UnsupportedOperationException();
678 }
679
680 @Override
681 public DataInput getGeneralBloomFilterMetadata() throws IOException {
682
683
684 ByteBuffer buf = getMetaBlock(HFileWriterV1.BLOOM_FILTER_META_KEY, false);
685 if (buf == null)
686 return null;
687 ByteArrayInputStream bais = new ByteArrayInputStream(buf.array(),
688 buf.arrayOffset(), buf.limit());
689 return new DataInputStream(bais);
690 }
691
692 @Override
693 public DataInput getDeleteBloomFilterMetadata() throws IOException {
694 return null;
695 }
696
697 @Override
698 public boolean isFileInfoLoaded() {
699 return fileInfoLoaded;
700 }
701
702 }