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
466
467
468
469
470
471
472
473
474 @edu.umd.cs.findbugs.annotations.SuppressWarnings(value="SF_SWITCH_FALLTHROUGH",
475 justification="Intentional")
476 private static Reader pickReaderVersion(Path path, FSDataInputStreamWrapper fsdis,
477 long size, CacheConfig cacheConf, HFileSystem hfs, Configuration conf) throws IOException {
478 FixedFileTrailer trailer = null;
479 try {
480 boolean isHBaseChecksum = fsdis.shouldUseHBaseChecksum();
481 assert !isHBaseChecksum;
482 trailer = FixedFileTrailer.readFromStream(fsdis.getStream(isHBaseChecksum), size);
483 switch (trailer.getMajorVersion()) {
484 case 2:
485 return new HFileReaderV2(path, trailer, fsdis, size, cacheConf, hfs, conf);
486 case 3 :
487 return new HFileReaderV3(path, trailer, fsdis, size, cacheConf, hfs, conf);
488 default:
489 throw new IllegalArgumentException("Invalid HFile version " + trailer.getMajorVersion());
490 }
491 } catch (Throwable t) {
492 try {
493 fsdis.close();
494 } catch (Throwable t2) {
495 LOG.warn("Error closing fsdis FSDataInputStreamWrapper", t2);
496 }
497 throw new CorruptHFileException("Problem reading HFile Trailer from file " + path, t);
498 }
499 }
500
501
502
503
504
505
506
507
508
509
510
511 public static Reader createReader(FileSystem fs, Path path,
512 FSDataInputStreamWrapper fsdis, long size, CacheConfig cacheConf, Configuration conf)
513 throws IOException {
514 HFileSystem hfs = null;
515
516
517
518
519
520 if (!(fs instanceof HFileSystem)) {
521 hfs = new HFileSystem(fs);
522 } else {
523 hfs = (HFileSystem)fs;
524 }
525 return pickReaderVersion(path, fsdis, size, cacheConf, hfs, conf);
526 }
527
528
529
530
531
532
533
534
535
536 public static Reader createReader(
537 FileSystem fs, Path path, CacheConfig cacheConf, Configuration conf) throws IOException {
538 Preconditions.checkNotNull(cacheConf, "Cannot create Reader with null CacheConf");
539 FSDataInputStreamWrapper stream = new FSDataInputStreamWrapper(fs, path);
540 return pickReaderVersion(path, stream, fs.getFileStatus(path).getLen(),
541 cacheConf, stream.getHfs(), conf);
542 }
543
544
545
546
547 static Reader createReaderFromStream(Path path,
548 FSDataInputStream fsdis, long size, CacheConfig cacheConf, Configuration conf)
549 throws IOException {
550 FSDataInputStreamWrapper wrapper = new FSDataInputStreamWrapper(fsdis);
551 return pickReaderVersion(path, wrapper, size, cacheConf, null, conf);
552 }
553
554
555
556
557
558
559
560
561 public static boolean isHFileFormat(final FileSystem fs, final Path path) throws IOException {
562 return isHFileFormat(fs, fs.getFileStatus(path));
563 }
564
565
566
567
568
569
570
571
572 public static boolean isHFileFormat(final FileSystem fs, final FileStatus fileStatus)
573 throws IOException {
574 final Path path = fileStatus.getPath();
575 final long size = fileStatus.getLen();
576 FSDataInputStreamWrapper fsdis = new FSDataInputStreamWrapper(fs, path);
577 try {
578 boolean isHBaseChecksum = fsdis.shouldUseHBaseChecksum();
579 assert !isHBaseChecksum;
580 FixedFileTrailer.readFromStream(fsdis.getStream(isHBaseChecksum), size);
581 return true;
582 } catch (IllegalArgumentException e) {
583 return false;
584 } catch (IOException e) {
585 throw e;
586 } finally {
587 try {
588 fsdis.close();
589 } catch (Throwable t) {
590 LOG.warn("Error closing fsdis FSDataInputStreamWrapper: " + path, t);
591 }
592 }
593 }
594
595
596
597
598 public static class FileInfo implements SortedMap<byte[], byte[]> {
599 static final String RESERVED_PREFIX = "hfile.";
600 static final byte[] RESERVED_PREFIX_BYTES = Bytes.toBytes(RESERVED_PREFIX);
601 static final byte [] LASTKEY = Bytes.toBytes(RESERVED_PREFIX + "LASTKEY");
602 static final byte [] AVG_KEY_LEN = Bytes.toBytes(RESERVED_PREFIX + "AVG_KEY_LEN");
603 static final byte [] AVG_VALUE_LEN = Bytes.toBytes(RESERVED_PREFIX + "AVG_VALUE_LEN");
604 static final byte [] CREATE_TIME_TS = Bytes.toBytes(RESERVED_PREFIX + "CREATE_TIME_TS");
605 static final byte [] COMPARATOR = Bytes.toBytes(RESERVED_PREFIX + "COMPARATOR");
606 static final byte [] TAGS_COMPRESSED = Bytes.toBytes(RESERVED_PREFIX + "TAGS_COMPRESSED");
607 public static final byte [] MAX_TAGS_LEN = Bytes.toBytes(RESERVED_PREFIX + "MAX_TAGS_LEN");
608 private final SortedMap<byte [], byte []> map = new TreeMap<byte [], byte []>(Bytes.BYTES_COMPARATOR);
609
610 public FileInfo() {
611 super();
612 }
613
614
615
616
617
618
619
620
621
622
623
624
625 public FileInfo append(final byte[] k, final byte[] v,
626 final boolean checkPrefix) throws IOException {
627 if (k == null || v == null) {
628 throw new NullPointerException("Key nor value may be null");
629 }
630 if (checkPrefix && isReservedFileInfoKey(k)) {
631 throw new IOException("Keys with a " + FileInfo.RESERVED_PREFIX
632 + " are reserved");
633 }
634 put(k, v);
635 return this;
636 }
637
638 public void clear() {
639 this.map.clear();
640 }
641
642 public Comparator<? super byte[]> comparator() {
643 return map.comparator();
644 }
645
646 public boolean containsKey(Object key) {
647 return map.containsKey(key);
648 }
649
650 public boolean containsValue(Object value) {
651 return map.containsValue(value);
652 }
653
654 public Set<java.util.Map.Entry<byte[], byte[]>> entrySet() {
655 return map.entrySet();
656 }
657
658 public boolean equals(Object o) {
659 return map.equals(o);
660 }
661
662 public byte[] firstKey() {
663 return map.firstKey();
664 }
665
666 public byte[] get(Object key) {
667 return map.get(key);
668 }
669
670 public int hashCode() {
671 return map.hashCode();
672 }
673
674 public SortedMap<byte[], byte[]> headMap(byte[] toKey) {
675 return this.map.headMap(toKey);
676 }
677
678 public boolean isEmpty() {
679 return map.isEmpty();
680 }
681
682 public Set<byte[]> keySet() {
683 return map.keySet();
684 }
685
686 public byte[] lastKey() {
687 return map.lastKey();
688 }
689
690 public byte[] put(byte[] key, byte[] value) {
691 return this.map.put(key, value);
692 }
693
694 public void putAll(Map<? extends byte[], ? extends byte[]> m) {
695 this.map.putAll(m);
696 }
697
698 public byte[] remove(Object key) {
699 return this.map.remove(key);
700 }
701
702 public int size() {
703 return map.size();
704 }
705
706 public SortedMap<byte[], byte[]> subMap(byte[] fromKey, byte[] toKey) {
707 return this.map.subMap(fromKey, toKey);
708 }
709
710 public SortedMap<byte[], byte[]> tailMap(byte[] fromKey) {
711 return this.map.tailMap(fromKey);
712 }
713
714 public Collection<byte[]> values() {
715 return map.values();
716 }
717
718
719
720
721
722
723
724
725 void write(final DataOutputStream out) throws IOException {
726 HFileProtos.FileInfoProto.Builder builder = HFileProtos.FileInfoProto.newBuilder();
727 for (Map.Entry<byte [], byte[]> e: this.map.entrySet()) {
728 HBaseProtos.BytesBytesPair.Builder bbpBuilder = HBaseProtos.BytesBytesPair.newBuilder();
729 bbpBuilder.setFirst(ByteStringer.wrap(e.getKey()));
730 bbpBuilder.setSecond(ByteStringer.wrap(e.getValue()));
731 builder.addMapEntry(bbpBuilder.build());
732 }
733 out.write(ProtobufUtil.PB_MAGIC);
734 builder.build().writeDelimitedTo(out);
735 }
736
737
738
739
740
741
742
743
744 void read(final DataInputStream in) throws IOException {
745
746 int pblen = ProtobufUtil.lengthOfPBMagic();
747 byte [] pbuf = new byte[pblen];
748 if (in.markSupported()) in.mark(pblen);
749 int read = in.read(pbuf);
750 if (read != pblen) throw new IOException("read=" + read + ", wanted=" + pblen);
751 if (ProtobufUtil.isPBMagicPrefix(pbuf)) {
752 parsePB(HFileProtos.FileInfoProto.parseDelimitedFrom(in));
753 } else {
754 if (in.markSupported()) {
755 in.reset();
756 parseWritable(in);
757 } else {
758
759 ByteArrayInputStream bais = new ByteArrayInputStream(pbuf);
760 SequenceInputStream sis = new SequenceInputStream(bais, in);
761
762
763
764 parseWritable(new DataInputStream(sis));
765 }
766 }
767 }
768
769
770
771
772
773
774 void parseWritable(final DataInputStream in) throws IOException {
775
776 this.map.clear();
777
778 int entries = in.readInt();
779
780 for (int i = 0; i < entries; i++) {
781 byte [] key = Bytes.readByteArray(in);
782
783 in.readByte();
784 byte [] value = Bytes.readByteArray(in);
785 this.map.put(key, value);
786 }
787 }
788
789
790
791
792
793 void parsePB(final HFileProtos.FileInfoProto fip) {
794 this.map.clear();
795 for (BytesBytesPair pair: fip.getMapEntryList()) {
796 this.map.put(pair.getFirst().toByteArray(), pair.getSecond().toByteArray());
797 }
798 }
799 }
800
801
802 public static boolean isReservedFileInfoKey(byte[] key) {
803 return Bytes.startsWith(key, FileInfo.RESERVED_PREFIX_BYTES);
804 }
805
806
807
808
809
810
811
812
813
814
815
816
817
818 public static String[] getSupportedCompressionAlgorithms() {
819 return Compression.getSupportedAlgorithms();
820 }
821
822
823
824
825
826
827 static int longToInt(final long l) {
828
829
830 return (int)(l & 0x00000000ffffffffL);
831 }
832
833
834
835
836
837
838
839
840
841
842 static List<Path> getStoreFiles(FileSystem fs, Path regionDir)
843 throws IOException {
844 List<Path> regionHFiles = new ArrayList<Path>();
845 PathFilter dirFilter = new FSUtils.DirFilter(fs);
846 FileStatus[] familyDirs = fs.listStatus(regionDir, dirFilter);
847 for(FileStatus dir : familyDirs) {
848 FileStatus[] files = fs.listStatus(dir.getPath());
849 for (FileStatus file : files) {
850 if (!file.isDirectory() &&
851 (!file.getPath().toString().contains(HConstants.HREGION_OLDLOGDIR_NAME)) &&
852 (!file.getPath().toString().contains(HConstants.RECOVERED_EDITS_DIR))) {
853 regionHFiles.add(file.getPath());
854 }
855 }
856 }
857 return regionHFiles;
858 }
859
860
861
862
863
864
865
866
867
868
869 public static void checkFormatVersion(int version)
870 throws IllegalArgumentException {
871 if (version < MIN_FORMAT_VERSION || version > MAX_FORMAT_VERSION) {
872 throw new IllegalArgumentException("Invalid HFile version: " + version
873 + " (expected to be " + "between " + MIN_FORMAT_VERSION + " and "
874 + MAX_FORMAT_VERSION + ")");
875 }
876 }
877
878 public static void main(String[] args) throws Exception {
879
880 HFilePrettyPrinter.main(args);
881 }
882 }