1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.hadoop.hbase.io.hfile;
20
21 import java.io.ByteArrayInputStream;
22 import java.io.Closeable;
23 import java.io.DataInput;
24 import java.io.DataInputStream;
25 import java.io.DataOutputStream;
26 import java.io.IOException;
27 import java.io.SequenceInputStream;
28 import java.net.InetSocketAddress;
29 import java.nio.ByteBuffer;
30 import java.util.ArrayList;
31 import java.util.Collection;
32 import java.util.Comparator;
33 import java.util.List;
34 import java.util.Map;
35 import java.util.Set;
36 import java.util.SortedMap;
37 import java.util.TreeMap;
38
39 import org.apache.commons.logging.Log;
40 import org.apache.commons.logging.LogFactory;
41 import org.apache.hadoop.hbase.classification.InterfaceAudience;
42 import org.apache.hadoop.conf.Configuration;
43 import org.apache.hadoop.fs.FSDataInputStream;
44 import org.apache.hadoop.fs.FSDataOutputStream;
45 import org.apache.hadoop.fs.FileStatus;
46 import org.apache.hadoop.fs.FileSystem;
47 import org.apache.hadoop.fs.Path;
48 import org.apache.hadoop.fs.PathFilter;
49 import org.apache.hadoop.hbase.Cell;
50 import org.apache.hadoop.hbase.HConstants;
51 import org.apache.hadoop.hbase.KeyValue;
52 import org.apache.hadoop.hbase.KeyValue.KVComparator;
53 import org.apache.hadoop.hbase.fs.HFileSystem;
54 import org.apache.hadoop.hbase.io.FSDataInputStreamWrapper;
55 import org.apache.hadoop.hbase.io.compress.Compression;
56 import org.apache.hadoop.hbase.io.encoding.DataBlockEncoding;
57 import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
58 import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos;
59 import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.BytesBytesPair;
60 import org.apache.hadoop.hbase.protobuf.generated.HFileProtos;
61 import org.apache.hadoop.hbase.util.BloomFilterWriter;
62 import org.apache.hadoop.hbase.util.ByteStringer;
63 import org.apache.hadoop.hbase.util.Bytes;
64 import org.apache.hadoop.hbase.util.Counter;
65 import org.apache.hadoop.hbase.util.FSUtils;
66 import org.apache.hadoop.io.Writable;
67
68 import com.google.common.base.Preconditions;
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134 @InterfaceAudience.Private
135 public class HFile {
136
137 static final Log LOG = LogFactory.getLog(HFile.class);
138
139
140
141
142 public final static int MAXIMUM_KEY_LENGTH = Integer.MAX_VALUE;
143
144
145
146
147 public final static Compression.Algorithm DEFAULT_COMPRESSION_ALGORITHM =
148 Compression.Algorithm.NONE;
149
150
151 public static final int MIN_FORMAT_VERSION = 2;
152
153
154
155 public static final int MAX_FORMAT_VERSION = 3;
156
157
158
159
160 public static final int MIN_FORMAT_VERSION_WITH_TAGS = 3;
161
162
163 public final static String DEFAULT_COMPRESSION =
164 DEFAULT_COMPRESSION_ALGORITHM.getName();
165
166
167 public static final String BLOOM_FILTER_DATA_KEY = "BLOOM_FILTER_DATA";
168
169
170
171
172
173
174
175 public final static int MIN_NUM_HFILE_PATH_LEVELS = 5;
176
177
178
179
180 public static final int DEFAULT_BYTES_PER_CHECKSUM = 16 * 1024;
181
182
183 static final Counter CHECKSUM_FAILURES = new Counter();
184
185
186 public static final Counter DATABLOCK_READ_COUNT = new Counter();
187
188
189
190
191
192 public static final long getChecksumFailuresCount() {
193 long count = CHECKSUM_FAILURES.get();
194 CHECKSUM_FAILURES.set(0);
195 return count;
196 }
197
198
199 public interface Writer extends Closeable {
200
201
202 void appendFileInfo(byte[] key, byte[] value) throws IOException;
203
204 void append(Cell cell) throws IOException;
205
206
207 Path getPath();
208
209
210
211
212
213 void addInlineBlockWriter(InlineBlockWriter bloomWriter);
214
215
216
217
218
219
220 void appendMetaBlock(String bloomFilterMetaKey, Writable metaWriter);
221
222
223
224
225
226
227 void addGeneralBloomFilter(BloomFilterWriter bfw);
228
229
230
231
232
233 void addDeleteFamilyBloomFilter(BloomFilterWriter bfw) throws IOException;
234
235
236
237
238 HFileContext getFileContext();
239 }
240
241
242
243
244
245 public static abstract class WriterFactory {
246 protected final Configuration conf;
247 protected final CacheConfig cacheConf;
248 protected FileSystem fs;
249 protected Path path;
250 protected FSDataOutputStream ostream;
251 protected KVComparator comparator = KeyValue.COMPARATOR;
252 protected InetSocketAddress[] favoredNodes;
253 private HFileContext fileContext;
254 protected boolean shouldDropBehind = false;
255
256 WriterFactory(Configuration conf, CacheConfig cacheConf) {
257 this.conf = conf;
258 this.cacheConf = cacheConf;
259 }
260
261 public WriterFactory withPath(FileSystem fs, Path path) {
262 Preconditions.checkNotNull(fs);
263 Preconditions.checkNotNull(path);
264 this.fs = fs;
265 this.path = path;
266 return this;
267 }
268
269 public WriterFactory withOutputStream(FSDataOutputStream ostream) {
270 Preconditions.checkNotNull(ostream);
271 this.ostream = ostream;
272 return this;
273 }
274
275 public WriterFactory withComparator(KVComparator comparator) {
276 Preconditions.checkNotNull(comparator);
277 this.comparator = comparator;
278 return this;
279 }
280
281 public WriterFactory withFavoredNodes(InetSocketAddress[] favoredNodes) {
282
283 this.favoredNodes = favoredNodes;
284 return this;
285 }
286
287 public WriterFactory withFileContext(HFileContext fileContext) {
288 this.fileContext = fileContext;
289 return this;
290 }
291
292 public WriterFactory withShouldDropCacheBehind(boolean shouldDropBehind) {
293 this.shouldDropBehind = shouldDropBehind;
294 return this;
295 }
296
297
298 public Writer create() throws IOException {
299 if ((path != null ? 1 : 0) + (ostream != null ? 1 : 0) != 1) {
300 throw new AssertionError("Please specify exactly one of " +
301 "filesystem/path or path");
302 }
303 if (path != null) {
304 ostream = AbstractHFileWriter.createOutputStream(conf, fs, path, favoredNodes);
305 try {
306 ostream.setDropBehind(shouldDropBehind && cacheConf.shouldDropBehindCompaction());
307 } catch (UnsupportedOperationException uoe) {
308 if (LOG.isTraceEnabled()) LOG.trace("Unable to set drop behind on " + path, uoe);
309 else if (LOG.isDebugEnabled()) LOG.debug("Unable to set drop behind on " + path);
310 }
311 }
312 return createWriter(fs, path, ostream,
313 comparator, fileContext);
314 }
315
316 protected abstract Writer createWriter(FileSystem fs, Path path, FSDataOutputStream ostream,
317 KVComparator comparator, HFileContext fileContext) throws IOException;
318 }
319
320
321 public static final String FORMAT_VERSION_KEY = "hfile.format.version";
322
323 public static int getFormatVersion(Configuration conf) {
324 int version = conf.getInt(FORMAT_VERSION_KEY, MAX_FORMAT_VERSION);
325 checkFormatVersion(version);
326 return version;
327 }
328
329
330
331
332
333
334 public static final WriterFactory getWriterFactoryNoCache(Configuration
335 conf) {
336 Configuration tempConf = new Configuration(conf);
337 tempConf.setFloat(HConstants.HFILE_BLOCK_CACHE_SIZE_KEY, 0.0f);
338 return HFile.getWriterFactory(conf, new CacheConfig(tempConf));
339 }
340
341
342
343
344 public static final WriterFactory getWriterFactory(Configuration conf,
345 CacheConfig cacheConf) {
346 int version = getFormatVersion(conf);
347 switch (version) {
348 case 2:
349 return new HFileWriterV2.WriterFactoryV2(conf, cacheConf);
350 case 3:
351 return new HFileWriterV3.WriterFactoryV3(conf, cacheConf);
352 default:
353 throw new IllegalArgumentException("Cannot create writer for HFile " +
354 "format version " + version);
355 }
356 }
357
358
359
360
361
362
363 public interface CachingBlockReader {
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381 HFileBlock readBlock(long offset, long onDiskBlockSize,
382 boolean cacheBlock, final boolean pread, final boolean isCompaction,
383 final boolean updateCacheMetrics, BlockType expectedBlockType,
384 DataBlockEncoding expectedDataBlockEncoding)
385 throws IOException;
386 }
387
388
389 public interface Reader extends Closeable, CachingBlockReader {
390
391
392
393
394
395 String getName();
396
397 KVComparator getComparator();
398
399 HFileScanner getScanner(boolean cacheBlocks, final boolean pread, final boolean isCompaction);
400
401 ByteBuffer getMetaBlock(String metaBlockName, boolean cacheBlock) throws IOException;
402
403 Map<byte[], byte[]> loadFileInfo() throws IOException;
404
405 byte[] getLastKey();
406
407 byte[] midkey() throws IOException;
408
409 long length();
410
411 long getEntries();
412
413 byte[] getFirstKey();
414
415 long indexSize();
416
417 byte[] getFirstRowKey();
418
419 byte[] getLastRowKey();
420
421 FixedFileTrailer getTrailer();
422
423 HFileBlockIndex.BlockIndexReader getDataBlockIndexReader();
424
425 HFileScanner getScanner(boolean cacheBlocks, boolean pread);
426
427 Compression.Algorithm getCompressionAlgorithm();
428
429
430
431
432
433
434 DataInput getGeneralBloomFilterMetadata() throws IOException;
435
436
437
438
439
440
441 DataInput getDeleteBloomFilterMetadata() throws IOException;
442
443 Path getPath();
444
445
446 void close(boolean evictOnClose) throws IOException;
447
448 DataBlockEncoding getDataBlockEncoding();
449
450 boolean hasMVCCInfo();
451
452
453
454
455 HFileContext getFileContext();
456
457 boolean isPrimaryReplicaReader();
458
459 void setPrimaryReplicaReader(boolean isPrimaryReplicaReader);
460
461
462
463
464
465 void unbufferStream();
466 }
467
468
469
470
471
472
473
474
475
476
477
478
479
480 @edu.umd.cs.findbugs.annotations.SuppressWarnings(value="SF_SWITCH_FALLTHROUGH",
481 justification="Intentional")
482 private static Reader openReader(Path path, FSDataInputStreamWrapper fsdis, long size,
483 CacheConfig cacheConf, HFileSystem hfs, Configuration conf) throws IOException {
484 FixedFileTrailer trailer = null;
485 try {
486 boolean isHBaseChecksum = fsdis.shouldUseHBaseChecksum();
487 assert !isHBaseChecksum;
488 trailer = FixedFileTrailer.readFromStream(fsdis.getStream(isHBaseChecksum), size);
489 switch (trailer.getMajorVersion()) {
490 case 2:
491 return new HFileReaderV2(path, trailer, fsdis, size, cacheConf, hfs, conf);
492 case 3 :
493 return new HFileReaderV3(path, trailer, fsdis, size, cacheConf, hfs, conf);
494 default:
495 throw new IllegalArgumentException("Invalid HFile version " + trailer.getMajorVersion());
496 }
497 } catch (Throwable t) {
498 try {
499 fsdis.close();
500 } catch (Throwable t2) {
501 LOG.warn("Error closing fsdis FSDataInputStreamWrapper", t2);
502 }
503 throw new CorruptHFileException("Problem reading HFile Trailer from file " + path, t);
504 } finally {
505 fsdis.unbuffer();
506 }
507 }
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522 public static Reader createReader(FileSystem fs, Path path,
523 FSDataInputStreamWrapper fsdis, long size, CacheConfig cacheConf, Configuration conf)
524 throws IOException {
525 HFileSystem hfs = null;
526
527
528
529
530
531 if (!(fs instanceof HFileSystem)) {
532 hfs = new HFileSystem(fs);
533 } else {
534 hfs = (HFileSystem)fs;
535 }
536 return openReader(path, fsdis, size, cacheConf, hfs, conf);
537 }
538
539
540
541
542
543
544
545
546
547 public static Reader createReader(
548 FileSystem fs, Path path, CacheConfig cacheConf, Configuration conf) throws IOException {
549 Preconditions.checkNotNull(cacheConf, "Cannot create Reader with null CacheConf");
550 FSDataInputStreamWrapper stream = new FSDataInputStreamWrapper(fs, path);
551 return openReader(path, stream, fs.getFileStatus(path).getLen(),
552 cacheConf, stream.getHfs(), conf);
553 }
554
555
556
557
558
559
560
561 static Reader createReaderFromStream(Path path,
562 FSDataInputStream fsdis, long size, CacheConfig cacheConf, Configuration conf)
563 throws IOException {
564 FSDataInputStreamWrapper wrapper = new FSDataInputStreamWrapper(fsdis);
565 return openReader(path, wrapper, size, cacheConf, null, conf);
566 }
567
568
569
570
571
572
573
574
575 public static boolean isHFileFormat(final FileSystem fs, final Path path) throws IOException {
576 return isHFileFormat(fs, fs.getFileStatus(path));
577 }
578
579
580
581
582
583
584
585
586 public static boolean isHFileFormat(final FileSystem fs, final FileStatus fileStatus)
587 throws IOException {
588 final Path path = fileStatus.getPath();
589 final long size = fileStatus.getLen();
590 FSDataInputStreamWrapper fsdis = new FSDataInputStreamWrapper(fs, path);
591 try {
592 boolean isHBaseChecksum = fsdis.shouldUseHBaseChecksum();
593 assert !isHBaseChecksum;
594 FixedFileTrailer.readFromStream(fsdis.getStream(isHBaseChecksum), size);
595 return true;
596 } catch (IllegalArgumentException e) {
597 return false;
598 } catch (IOException e) {
599 throw e;
600 } finally {
601 try {
602 fsdis.close();
603 } catch (Throwable t) {
604 LOG.warn("Error closing fsdis FSDataInputStreamWrapper: " + path, t);
605 }
606 }
607 }
608
609
610
611
612 public static class FileInfo implements SortedMap<byte[], byte[]> {
613 static final String RESERVED_PREFIX = "hfile.";
614 static final byte[] RESERVED_PREFIX_BYTES = Bytes.toBytes(RESERVED_PREFIX);
615 static final byte [] LASTKEY = Bytes.toBytes(RESERVED_PREFIX + "LASTKEY");
616 static final byte [] AVG_KEY_LEN = Bytes.toBytes(RESERVED_PREFIX + "AVG_KEY_LEN");
617 static final byte [] AVG_VALUE_LEN = Bytes.toBytes(RESERVED_PREFIX + "AVG_VALUE_LEN");
618 static final byte [] CREATE_TIME_TS = Bytes.toBytes(RESERVED_PREFIX + "CREATE_TIME_TS");
619 static final byte [] COMPARATOR = Bytes.toBytes(RESERVED_PREFIX + "COMPARATOR");
620 static final byte [] TAGS_COMPRESSED = Bytes.toBytes(RESERVED_PREFIX + "TAGS_COMPRESSED");
621 public static final byte [] MAX_TAGS_LEN = Bytes.toBytes(RESERVED_PREFIX + "MAX_TAGS_LEN");
622 private final SortedMap<byte [], byte []> map = new TreeMap<byte [], byte []>(Bytes.BYTES_COMPARATOR);
623
624 public FileInfo() {
625 super();
626 }
627
628
629
630
631
632
633
634
635
636
637
638
639 public FileInfo append(final byte[] k, final byte[] v,
640 final boolean checkPrefix) throws IOException {
641 if (k == null || v == null) {
642 throw new NullPointerException("Key nor value may be null");
643 }
644 if (checkPrefix && isReservedFileInfoKey(k)) {
645 throw new IOException("Keys with a " + FileInfo.RESERVED_PREFIX
646 + " are reserved");
647 }
648 put(k, v);
649 return this;
650 }
651
652 public void clear() {
653 this.map.clear();
654 }
655
656 public Comparator<? super byte[]> comparator() {
657 return map.comparator();
658 }
659
660 public boolean containsKey(Object key) {
661 return map.containsKey(key);
662 }
663
664 public boolean containsValue(Object value) {
665 return map.containsValue(value);
666 }
667
668 public Set<java.util.Map.Entry<byte[], byte[]>> entrySet() {
669 return map.entrySet();
670 }
671
672 public boolean equals(Object o) {
673 return map.equals(o);
674 }
675
676 public byte[] firstKey() {
677 return map.firstKey();
678 }
679
680 public byte[] get(Object key) {
681 return map.get(key);
682 }
683
684 public int hashCode() {
685 return map.hashCode();
686 }
687
688 public SortedMap<byte[], byte[]> headMap(byte[] toKey) {
689 return this.map.headMap(toKey);
690 }
691
692 public boolean isEmpty() {
693 return map.isEmpty();
694 }
695
696 public Set<byte[]> keySet() {
697 return map.keySet();
698 }
699
700 public byte[] lastKey() {
701 return map.lastKey();
702 }
703
704 public byte[] put(byte[] key, byte[] value) {
705 return this.map.put(key, value);
706 }
707
708 public void putAll(Map<? extends byte[], ? extends byte[]> m) {
709 this.map.putAll(m);
710 }
711
712 public byte[] remove(Object key) {
713 return this.map.remove(key);
714 }
715
716 public int size() {
717 return map.size();
718 }
719
720 public SortedMap<byte[], byte[]> subMap(byte[] fromKey, byte[] toKey) {
721 return this.map.subMap(fromKey, toKey);
722 }
723
724 public SortedMap<byte[], byte[]> tailMap(byte[] fromKey) {
725 return this.map.tailMap(fromKey);
726 }
727
728 public Collection<byte[]> values() {
729 return map.values();
730 }
731
732
733
734
735
736
737
738
739 void write(final DataOutputStream out) throws IOException {
740 HFileProtos.FileInfoProto.Builder builder = HFileProtos.FileInfoProto.newBuilder();
741 for (Map.Entry<byte [], byte[]> e: this.map.entrySet()) {
742 HBaseProtos.BytesBytesPair.Builder bbpBuilder = HBaseProtos.BytesBytesPair.newBuilder();
743 bbpBuilder.setFirst(ByteStringer.wrap(e.getKey()));
744 bbpBuilder.setSecond(ByteStringer.wrap(e.getValue()));
745 builder.addMapEntry(bbpBuilder.build());
746 }
747 out.write(ProtobufUtil.PB_MAGIC);
748 builder.build().writeDelimitedTo(out);
749 }
750
751
752
753
754
755
756
757
758 void read(final DataInputStream in) throws IOException {
759
760 int pblen = ProtobufUtil.lengthOfPBMagic();
761 byte [] pbuf = new byte[pblen];
762 if (in.markSupported()) in.mark(pblen);
763 int read = in.read(pbuf);
764 if (read != pblen) throw new IOException("read=" + read + ", wanted=" + pblen);
765 if (ProtobufUtil.isPBMagicPrefix(pbuf)) {
766 parsePB(HFileProtos.FileInfoProto.parseDelimitedFrom(in));
767 } else {
768 if (in.markSupported()) {
769 in.reset();
770 parseWritable(in);
771 } else {
772
773 ByteArrayInputStream bais = new ByteArrayInputStream(pbuf);
774 SequenceInputStream sis = new SequenceInputStream(bais, in);
775
776
777
778 parseWritable(new DataInputStream(sis));
779 }
780 }
781 }
782
783
784
785
786
787
788 void parseWritable(final DataInputStream in) throws IOException {
789
790 this.map.clear();
791
792 int entries = in.readInt();
793
794 for (int i = 0; i < entries; i++) {
795 byte [] key = Bytes.readByteArray(in);
796
797 in.readByte();
798 byte [] value = Bytes.readByteArray(in);
799 this.map.put(key, value);
800 }
801 }
802
803
804
805
806
807 void parsePB(final HFileProtos.FileInfoProto fip) {
808 this.map.clear();
809 for (BytesBytesPair pair: fip.getMapEntryList()) {
810 this.map.put(pair.getFirst().toByteArray(), pair.getSecond().toByteArray());
811 }
812 }
813 }
814
815
816 public static boolean isReservedFileInfoKey(byte[] key) {
817 return Bytes.startsWith(key, FileInfo.RESERVED_PREFIX_BYTES);
818 }
819
820
821
822
823
824
825
826
827
828
829
830
831
832 public static String[] getSupportedCompressionAlgorithms() {
833 return Compression.getSupportedAlgorithms();
834 }
835
836
837
838
839
840
841 static int longToInt(final long l) {
842
843
844 return (int)(l & 0x00000000ffffffffL);
845 }
846
847
848
849
850
851
852
853
854
855
856 static List<Path> getStoreFiles(FileSystem fs, Path regionDir)
857 throws IOException {
858 List<Path> regionHFiles = new ArrayList<Path>();
859 PathFilter dirFilter = new FSUtils.DirFilter(fs);
860 FileStatus[] familyDirs = fs.listStatus(regionDir, dirFilter);
861 for(FileStatus dir : familyDirs) {
862 FileStatus[] files = fs.listStatus(dir.getPath());
863 for (FileStatus file : files) {
864 if (!file.isDirectory() &&
865 (!file.getPath().toString().contains(HConstants.HREGION_OLDLOGDIR_NAME)) &&
866 (!file.getPath().toString().contains(HConstants.RECOVERED_EDITS_DIR))) {
867 regionHFiles.add(file.getPath());
868 }
869 }
870 }
871 return regionHFiles;
872 }
873
874
875
876
877
878
879
880
881
882
883 public static void checkFormatVersion(int version)
884 throws IllegalArgumentException {
885 if (version < MIN_FORMAT_VERSION || version > MAX_FORMAT_VERSION) {
886 throw new IllegalArgumentException("Invalid HFile version: " + version
887 + " (expected to be " + "between " + MIN_FORMAT_VERSION + " and "
888 + MAX_FORMAT_VERSION + ")");
889 }
890 }
891
892 public static void main(String[] args) throws Exception {
893
894 HFilePrettyPrinter.main(args);
895 }
896 }