1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.hadoop.hbase.regionserver;
20
21 import java.io.DataInput;
22 import java.io.IOException;
23 import java.net.InetSocketAddress;
24 import java.nio.ByteBuffer;
25 import java.util.Arrays;
26 import java.util.Collection;
27 import java.util.Collections;
28 import java.util.Comparator;
29 import java.util.Map;
30 import java.util.SortedSet;
31 import java.util.UUID;
32 import java.util.concurrent.atomic.AtomicBoolean;
33
34 import org.apache.commons.logging.Log;
35 import org.apache.commons.logging.LogFactory;
36 import org.apache.hadoop.conf.Configuration;
37 import org.apache.hadoop.fs.FileSystem;
38 import org.apache.hadoop.fs.Path;
39 import org.apache.hadoop.hbase.Cell;
40 import org.apache.hadoop.hbase.CellUtil;
41 import org.apache.hadoop.hbase.HConstants;
42 import org.apache.hadoop.hbase.HDFSBlocksDistribution;
43 import org.apache.hadoop.hbase.KeyValue;
44 import org.apache.hadoop.hbase.KeyValue.KVComparator;
45 import org.apache.hadoop.hbase.KeyValueUtil;
46 import org.apache.hadoop.hbase.classification.InterfaceAudience;
47 import org.apache.hadoop.hbase.client.Scan;
48 import org.apache.hadoop.hbase.io.FSDataInputStreamWrapper;
49 import org.apache.hadoop.hbase.io.TimeRange;
50 import org.apache.hadoop.hbase.io.hfile.BlockType;
51 import org.apache.hadoop.hbase.io.hfile.CacheConfig;
52 import org.apache.hadoop.hbase.io.hfile.HFile;
53 import org.apache.hadoop.hbase.io.hfile.HFileContext;
54 import org.apache.hadoop.hbase.io.hfile.HFileScanner;
55 import org.apache.hadoop.hbase.io.hfile.HFileWriterV2;
56 import org.apache.hadoop.hbase.regionserver.compactions.Compactor;
57 import org.apache.hadoop.hbase.util.BloomFilter;
58 import org.apache.hadoop.hbase.util.BloomFilterFactory;
59 import org.apache.hadoop.hbase.util.BloomFilterWriter;
60 import org.apache.hadoop.hbase.util.Bytes;
61 import org.apache.hadoop.hbase.util.Writables;
62 import org.apache.hadoop.io.WritableUtils;
63
64 import com.google.common.base.Function;
65 import com.google.common.base.Preconditions;
66 import com.google.common.collect.ImmutableList;
67 import com.google.common.collect.Ordering;
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82 @InterfaceAudience.LimitedPrivate("Coprocessor")
83 public class StoreFile {
84 private static final Log LOG = LogFactory.getLog(StoreFile.class.getName());
85
86
87
88
89 public static final byte [] MAX_SEQ_ID_KEY = Bytes.toBytes("MAX_SEQ_ID_KEY");
90
91
92 public static final byte[] MAJOR_COMPACTION_KEY =
93 Bytes.toBytes("MAJOR_COMPACTION_KEY");
94
95
96 public static final byte[] EXCLUDE_FROM_MINOR_COMPACTION_KEY =
97 Bytes.toBytes("EXCLUDE_FROM_MINOR_COMPACTION");
98
99
100 public static final byte[] BLOOM_FILTER_TYPE_KEY =
101 Bytes.toBytes("BLOOM_FILTER_TYPE");
102
103
104 public static final byte[] DELETE_FAMILY_COUNT =
105 Bytes.toBytes("DELETE_FAMILY_COUNT");
106
107
108 private static final byte[] LAST_BLOOM_KEY = Bytes.toBytes("LAST_BLOOM_KEY");
109
110
111 public static final byte[] TIMERANGE_KEY = Bytes.toBytes("TIMERANGE");
112
113
114 public static final byte[] EARLIEST_PUT_TS = Bytes.toBytes("EARLIEST_PUT_TS");
115
116
117 public static final byte[] MOB_CELLS_COUNT = Bytes.toBytes("MOB_CELLS_COUNT");
118
119 private final StoreFileInfo fileInfo;
120 private final FileSystem fs;
121
122
123 private final CacheConfig cacheConf;
124
125
126
127 private long sequenceid = -1;
128
129
130
131 private long maxMemstoreTS = -1;
132
133
134 private byte[] firstKey;
135
136 private byte[] lastKey;
137
138 private KVComparator comparator;
139
140 CacheConfig getCacheConf() {
141 return cacheConf;
142 }
143
144 public byte[] getFirstKey() {
145 return firstKey;
146 }
147
148 public byte[] getLastKey() {
149 return lastKey;
150 }
151
152 public KVComparator getComparator() {
153 return comparator;
154 }
155
156 public long getMaxMemstoreTS() {
157 return maxMemstoreTS;
158 }
159
160 public void setMaxMemstoreTS(long maxMemstoreTS) {
161 this.maxMemstoreTS = maxMemstoreTS;
162 }
163
164
165
166 private AtomicBoolean majorCompaction = null;
167
168
169
170 private boolean excludeFromMinorCompaction = false;
171
172
173 public static final byte[] BULKLOAD_TASK_KEY =
174 Bytes.toBytes("BULKLOAD_SOURCE_TASK");
175 public static final byte[] BULKLOAD_TIME_KEY =
176 Bytes.toBytes("BULKLOAD_TIMESTAMP");
177
178
179
180
181 private Map<byte[], byte[]> metadataMap;
182
183
184 private volatile Reader reader;
185
186
187
188
189
190 private final BloomType cfBloomType;
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207 public StoreFile(final FileSystem fs, final Path p, final Configuration conf,
208 final CacheConfig cacheConf, final BloomType cfBloomType) throws IOException {
209 this(fs, new StoreFileInfo(conf, fs, p), conf, cacheConf, cfBloomType);
210 }
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228 public StoreFile(final FileSystem fs, final StoreFileInfo fileInfo, final Configuration conf,
229 final CacheConfig cacheConf, final BloomType cfBloomType) throws IOException {
230 this.fs = fs;
231 this.fileInfo = fileInfo;
232 this.cacheConf = cacheConf;
233
234 if (BloomFilterFactory.isGeneralBloomEnabled(conf)) {
235 this.cfBloomType = cfBloomType;
236 } else {
237 LOG.info("Ignoring bloom filter check for file " + this.getPath() + ": " +
238 "cfBloomType=" + cfBloomType + " (disabled in config)");
239 this.cfBloomType = BloomType.NONE;
240 }
241 }
242
243
244
245
246
247 public StoreFile(final StoreFile other) {
248 this.fs = other.fs;
249 this.fileInfo = other.fileInfo;
250 this.cacheConf = other.cacheConf;
251 this.cfBloomType = other.cfBloomType;
252 }
253
254
255
256
257
258 public StoreFileInfo getFileInfo() {
259 return this.fileInfo;
260 }
261
262
263
264
265 public Path getPath() {
266 return this.fileInfo.getPath();
267 }
268
269
270
271
272 public Path getQualifiedPath() {
273 return this.fileInfo.getPath().makeQualified(fs);
274 }
275
276
277
278
279
280 public boolean isReference() {
281 return this.fileInfo.isReference();
282 }
283
284
285
286
287 public boolean isMajorCompaction() {
288 if (this.majorCompaction == null) {
289 throw new NullPointerException("This has not been set yet");
290 }
291 return this.majorCompaction.get();
292 }
293
294
295
296
297 public boolean excludeFromMinorCompaction() {
298 return this.excludeFromMinorCompaction;
299 }
300
301
302
303
304 public long getMaxSequenceId() {
305 return this.sequenceid;
306 }
307
308 public long getModificationTimeStamp() throws IOException {
309 return (fileInfo == null) ? 0 : fileInfo.getModificationTime();
310 }
311
312
313
314
315
316
317 public byte[] getMetadataValue(byte[] key) {
318 return metadataMap.get(key);
319 }
320
321
322
323
324
325
326
327
328
329 public static long getMaxMemstoreTSInList(Collection<StoreFile> sfs) {
330 long max = 0;
331 for (StoreFile sf : sfs) {
332 if (!sf.isBulkLoadResult()) {
333 max = Math.max(max, sf.getMaxMemstoreTS());
334 }
335 }
336 return max;
337 }
338
339
340
341
342
343
344
345
346 public static long getMaxSequenceIdInList(Collection<StoreFile> sfs) {
347 long max = 0;
348 for (StoreFile sf : sfs) {
349 max = Math.max(max, sf.getMaxSequenceId());
350 }
351 return max;
352 }
353
354
355
356
357
358
359
360
361
362
363
364 boolean isBulkLoadResult() {
365 boolean bulkLoadedHFile = false;
366 String fileName = this.getPath().getName();
367 int startPos = fileName.indexOf("SeqId_");
368 if (startPos != -1) {
369 bulkLoadedHFile = true;
370 }
371 return bulkLoadedHFile || metadataMap.containsKey(BULKLOAD_TIME_KEY);
372 }
373
374
375
376
377 public long getBulkLoadTimestamp() {
378 byte[] bulkLoadTimestamp = metadataMap.get(BULKLOAD_TIME_KEY);
379 return (bulkLoadTimestamp == null) ? 0 : Bytes.toLong(bulkLoadTimestamp);
380 }
381
382
383
384
385
386 public HDFSBlocksDistribution getHDFSBlockDistribution() {
387 return this.fileInfo.getHDFSBlockDistribution();
388 }
389
390
391
392
393
394
395
396 private Reader open(boolean canUseDropBehind) throws IOException {
397 if (this.reader != null) {
398 throw new IllegalAccessError("Already open");
399 }
400
401
402 this.reader = fileInfo.open(this.fs, this.cacheConf, canUseDropBehind);
403
404
405 metadataMap = Collections.unmodifiableMap(this.reader.loadFileInfo());
406
407
408 byte [] b = metadataMap.get(MAX_SEQ_ID_KEY);
409 if (b != null) {
410
411
412
413
414
415 this.sequenceid = Bytes.toLong(b);
416 if (fileInfo.isTopReference()) {
417 this.sequenceid += 1;
418 }
419 }
420
421 if (isBulkLoadResult()){
422
423
424 String fileName = this.getPath().getName();
425
426 int startPos = fileName.lastIndexOf("SeqId_");
427 if (startPos != -1) {
428 this.sequenceid = Long.parseLong(fileName.substring(startPos + 6,
429 fileName.indexOf('_', startPos + 6)));
430
431 if (fileInfo.isTopReference()) {
432 this.sequenceid += 1;
433 }
434 }
435 this.reader.setBulkLoaded(true);
436 }
437 this.reader.setSequenceID(this.sequenceid);
438
439 b = metadataMap.get(HFileWriterV2.MAX_MEMSTORE_TS_KEY);
440 if (b != null) {
441 this.maxMemstoreTS = Bytes.toLong(b);
442 }
443
444 b = metadataMap.get(MAJOR_COMPACTION_KEY);
445 if (b != null) {
446 boolean mc = Bytes.toBoolean(b);
447 if (this.majorCompaction == null) {
448 this.majorCompaction = new AtomicBoolean(mc);
449 } else {
450 this.majorCompaction.set(mc);
451 }
452 } else {
453
454
455 this.majorCompaction = new AtomicBoolean(false);
456 }
457
458 b = metadataMap.get(EXCLUDE_FROM_MINOR_COMPACTION_KEY);
459 this.excludeFromMinorCompaction = (b != null && Bytes.toBoolean(b));
460
461 BloomType hfileBloomType = reader.getBloomFilterType();
462 if (cfBloomType != BloomType.NONE) {
463 reader.loadBloomfilter(BlockType.GENERAL_BLOOM_META);
464 if (hfileBloomType != cfBloomType) {
465 LOG.info("HFile Bloom filter type for "
466 + reader.getHFileReader().getName() + ": " + hfileBloomType
467 + ", but " + cfBloomType + " specified in column family "
468 + "configuration");
469 }
470 } else if (hfileBloomType != BloomType.NONE) {
471 LOG.info("Bloom filter turned off by CF config for "
472 + reader.getHFileReader().getName());
473 }
474
475
476 reader.loadBloomfilter(BlockType.DELETE_FAMILY_BLOOM_META);
477
478 try {
479 byte [] timerangeBytes = metadataMap.get(TIMERANGE_KEY);
480 if (timerangeBytes != null) {
481 this.reader.timeRangeTracker = new TimeRangeTracker();
482 Writables.copyWritable(timerangeBytes, this.reader.timeRangeTracker);
483 }
484 } catch (IllegalArgumentException e) {
485 LOG.error("Error reading timestamp range data from meta -- " +
486 "proceeding without", e);
487 this.reader.timeRangeTracker = null;
488 }
489
490 firstKey = reader.getFirstKey();
491 lastKey = reader.getLastKey();
492 comparator = reader.getComparator();
493 return this.reader;
494 }
495
496 public Reader createReader() throws IOException {
497 return createReader(false);
498 }
499
500
501
502
503
504 public Reader createReader(boolean canUseDropBehind) throws IOException {
505 if (this.reader == null) {
506 try {
507 this.reader = open(canUseDropBehind);
508 } catch (IOException e) {
509 try {
510 boolean evictOnClose =
511 cacheConf != null? cacheConf.shouldEvictOnClose(): true;
512 this.closeReader(evictOnClose);
513 } catch (IOException ee) {
514 }
515 throw e;
516 }
517
518 }
519 return this.reader;
520 }
521
522
523
524
525
526 public Reader getReader() {
527 return this.reader;
528 }
529
530
531
532
533
534 public synchronized void closeReader(boolean evictOnClose)
535 throws IOException {
536 if (this.reader != null) {
537 this.reader.close(evictOnClose);
538 this.reader = null;
539 }
540 }
541
542
543
544
545
546 public void deleteReader() throws IOException {
547 boolean evictOnClose =
548 cacheConf != null? cacheConf.shouldEvictOnClose(): true;
549 closeReader(evictOnClose);
550 this.fs.delete(getPath(), true);
551 }
552
553 @Override
554 public String toString() {
555 return this.fileInfo.toString();
556 }
557
558
559
560
561 public String toStringDetailed() {
562 StringBuilder sb = new StringBuilder();
563 sb.append(this.getPath().toString());
564 sb.append(", isReference=").append(isReference());
565 sb.append(", isBulkLoadResult=").append(isBulkLoadResult());
566 if (isBulkLoadResult()) {
567 sb.append(", bulkLoadTS=").append(getBulkLoadTimestamp());
568 } else {
569 sb.append(", seqid=").append(getMaxSequenceId());
570 }
571 sb.append(", majorCompaction=").append(isMajorCompaction());
572
573 return sb.toString();
574 }
575
576 @edu.umd.cs.findbugs.annotations.SuppressWarnings(value="ICAST_INTEGER_MULTIPLY_CAST_TO_LONG",
577 justification="Will not overflow")
578 public static class WriterBuilder {
579 private final Configuration conf;
580 private final CacheConfig cacheConf;
581 private final FileSystem fs;
582
583 private KeyValue.KVComparator comparator = KeyValue.COMPARATOR;
584 private BloomType bloomType = BloomType.NONE;
585 private long maxKeyCount = 0;
586 private Path dir;
587 private Path filePath;
588 private InetSocketAddress[] favoredNodes;
589 private HFileContext fileContext;
590
591 public WriterBuilder(Configuration conf, CacheConfig cacheConf,
592 FileSystem fs) {
593 this.conf = conf;
594 this.cacheConf = cacheConf;
595 this.fs = fs;
596 }
597
598
599
600
601
602
603
604
605 public WriterBuilder withOutputDir(Path dir) {
606 Preconditions.checkNotNull(dir);
607 this.dir = dir;
608 return this;
609 }
610
611
612
613
614
615
616 public WriterBuilder withFilePath(Path filePath) {
617 Preconditions.checkNotNull(filePath);
618 this.filePath = filePath;
619 return this;
620 }
621
622
623
624
625
626 public WriterBuilder withFavoredNodes(InetSocketAddress[] favoredNodes) {
627 this.favoredNodes = favoredNodes;
628 return this;
629 }
630
631 public WriterBuilder withComparator(KeyValue.KVComparator comparator) {
632 Preconditions.checkNotNull(comparator);
633 this.comparator = comparator;
634 return this;
635 }
636
637 public WriterBuilder withBloomType(BloomType bloomType) {
638 Preconditions.checkNotNull(bloomType);
639 this.bloomType = bloomType;
640 return this;
641 }
642
643
644
645
646
647 public WriterBuilder withMaxKeyCount(long maxKeyCount) {
648 this.maxKeyCount = maxKeyCount;
649 return this;
650 }
651
652 public WriterBuilder withFileContext(HFileContext fileContext) {
653 this.fileContext = fileContext;
654 return this;
655 }
656
657 public WriterBuilder withShouldDropCacheBehind(boolean shouldDropCacheBehind
658
659 return this;
660 }
661
662
663
664
665
666 public Writer build() throws IOException {
667 if ((dir == null ? 0 : 1) + (filePath == null ? 0 : 1) != 1) {
668 throw new IllegalArgumentException("Either specify parent directory " +
669 "or file path");
670 }
671
672 if (dir == null) {
673 dir = filePath.getParent();
674 }
675
676 if (!fs.exists(dir)) {
677 fs.mkdirs(dir);
678 }
679
680 if (filePath == null) {
681 filePath = getUniqueFile(fs, dir);
682 if (!BloomFilterFactory.isGeneralBloomEnabled(conf)) {
683 bloomType = BloomType.NONE;
684 }
685 }
686
687 if (comparator == null) {
688 comparator = KeyValue.COMPARATOR;
689 }
690 return new Writer(fs, filePath,
691 conf, cacheConf, comparator, bloomType, maxKeyCount, favoredNodes, fileContext);
692 }
693 }
694
695
696
697
698
699
700 public static Path getUniqueFile(final FileSystem fs, final Path dir)
701 throws IOException {
702 if (!fs.getFileStatus(dir).isDirectory()) {
703 throw new IOException("Expecting " + dir.toString() +
704 " to be a directory");
705 }
706 return new Path(dir, UUID.randomUUID().toString().replaceAll("-", ""));
707 }
708
709 public Long getMinimumTimestamp() {
710 return (getReader().timeRangeTracker == null) ?
711 null :
712 getReader().timeRangeTracker.getMinimumTimestamp();
713 }
714
715
716
717
718
719
720 @SuppressWarnings("deprecation")
721 byte[] getFileSplitPoint(KVComparator comparator) throws IOException {
722 if (this.reader == null) {
723 LOG.warn("Storefile " + this + " Reader is null; cannot get split point");
724 return null;
725 }
726
727
728
729 byte [] midkey = this.reader.midkey();
730 if (midkey != null) {
731 KeyValue mk = KeyValue.createKeyValueFromKey(midkey, 0, midkey.length);
732 byte [] fk = this.reader.getFirstKey();
733 KeyValue firstKey = KeyValue.createKeyValueFromKey(fk, 0, fk.length);
734 byte [] lk = this.reader.getLastKey();
735 KeyValue lastKey = KeyValue.createKeyValueFromKey(lk, 0, lk.length);
736
737 if (comparator.compareRows(mk, firstKey) == 0 || comparator.compareRows(mk, lastKey) == 0) {
738 if (LOG.isDebugEnabled()) {
739 LOG.debug("cannot split because midkey is the same as first or last row");
740 }
741 return null;
742 }
743 return mk.getRow();
744 }
745 return null;
746 }
747
748
749
750
751
752 public static class Writer implements Compactor.CellSink {
753 private final BloomFilterWriter generalBloomFilterWriter;
754 private final BloomFilterWriter deleteFamilyBloomFilterWriter;
755 private final BloomType bloomType;
756 private byte[] lastBloomKey;
757 private int lastBloomKeyOffset, lastBloomKeyLen;
758 private KVComparator kvComparator;
759 private Cell lastCell = null;
760 private long earliestPutTs = HConstants.LATEST_TIMESTAMP;
761 private Cell lastDeleteFamilyCell = null;
762 private long deleteFamilyCnt = 0;
763
764 TimeRangeTracker timeRangeTracker = new TimeRangeTracker();
765
766
767
768
769
770
771 boolean isTimeRangeTrackerSet = false;
772
773 protected HFile.Writer writer;
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788 private Writer(FileSystem fs, Path path,
789 final Configuration conf,
790 CacheConfig cacheConf,
791 final KVComparator comparator, BloomType bloomType, long maxKeys,
792 InetSocketAddress[] favoredNodes, HFileContext fileContext)
793 throws IOException {
794 writer = HFile.getWriterFactory(conf, cacheConf)
795 .withPath(fs, path)
796 .withComparator(comparator)
797 .withFavoredNodes(favoredNodes)
798 .withFileContext(fileContext)
799 .create();
800
801 this.kvComparator = comparator;
802
803 generalBloomFilterWriter = BloomFilterFactory.createGeneralBloomAtWrite(
804 conf, cacheConf, bloomType,
805 (int) Math.min(maxKeys, Integer.MAX_VALUE), writer);
806
807 if (generalBloomFilterWriter != null) {
808 this.bloomType = bloomType;
809 if (LOG.isTraceEnabled()) LOG.trace("Bloom filter type for " + path + ": " +
810 this.bloomType + ", " + generalBloomFilterWriter.getClass().getSimpleName());
811 } else {
812
813 this.bloomType = BloomType.NONE;
814 }
815
816
817
818 if (this.bloomType != BloomType.ROWCOL) {
819 this.deleteFamilyBloomFilterWriter = BloomFilterFactory
820 .createDeleteBloomAtWrite(conf, cacheConf,
821 (int) Math.min(maxKeys, Integer.MAX_VALUE), writer);
822 } else {
823 deleteFamilyBloomFilterWriter = null;
824 }
825 if (deleteFamilyBloomFilterWriter != null) {
826 if (LOG.isTraceEnabled()) LOG.trace("Delete Family Bloom filter type for " + path + ": "
827 + deleteFamilyBloomFilterWriter.getClass().getSimpleName());
828 }
829 }
830
831
832
833
834
835
836
837
838 public void appendMetadata(final long maxSequenceId, final boolean majorCompaction)
839 throws IOException {
840 writer.appendFileInfo(MAX_SEQ_ID_KEY, Bytes.toBytes(maxSequenceId));
841 writer.appendFileInfo(MAJOR_COMPACTION_KEY,
842 Bytes.toBytes(majorCompaction));
843 appendTrackedTimestampsToMetadata();
844 }
845
846
847
848
849
850
851
852
853
854 public void appendMetadata(final long maxSequenceId, final boolean majorCompaction,
855 final long mobCellsCount) throws IOException {
856 writer.appendFileInfo(MAX_SEQ_ID_KEY, Bytes.toBytes(maxSequenceId));
857 writer.appendFileInfo(MAJOR_COMPACTION_KEY, Bytes.toBytes(majorCompaction));
858 writer.appendFileInfo(MOB_CELLS_COUNT, Bytes.toBytes(mobCellsCount));
859 appendTrackedTimestampsToMetadata();
860 }
861
862
863
864
865 public void appendTrackedTimestampsToMetadata() throws IOException {
866 appendFileInfo(TIMERANGE_KEY,WritableUtils.toByteArray(timeRangeTracker));
867 appendFileInfo(EARLIEST_PUT_TS, Bytes.toBytes(earliestPutTs));
868 }
869
870
871
872
873
874 public void setTimeRangeTracker(final TimeRangeTracker trt) {
875 this.timeRangeTracker = trt;
876 isTimeRangeTrackerSet = true;
877 }
878
879
880
881
882
883
884
885
886 public void trackTimestamps(final Cell cell) {
887 if (KeyValue.Type.Put.getCode() == cell.getTypeByte()) {
888 earliestPutTs = Math.min(earliestPutTs, cell.getTimestamp());
889 }
890 if (!isTimeRangeTrackerSet) {
891 timeRangeTracker.includeTimestamp(cell);
892 }
893 }
894
895 private void appendGeneralBloomfilter(final Cell cell) throws IOException {
896 if (this.generalBloomFilterWriter != null) {
897
898 boolean newKey = true;
899 if (this.lastCell != null) {
900 switch(bloomType) {
901 case ROW:
902 newKey = ! kvComparator.matchingRows(cell, lastCell);
903 break;
904 case ROWCOL:
905 newKey = ! kvComparator.matchingRowColumn(cell, lastCell);
906 break;
907 case NONE:
908 newKey = false;
909 break;
910 default:
911 throw new IOException("Invalid Bloom filter type: " + bloomType +
912 " (ROW or ROWCOL expected)");
913 }
914 }
915 if (newKey) {
916
917
918
919
920
921
922
923
924 byte[] bloomKey;
925 int bloomKeyOffset, bloomKeyLen;
926
927 switch (bloomType) {
928 case ROW:
929 bloomKey = cell.getRowArray();
930 bloomKeyOffset = cell.getRowOffset();
931 bloomKeyLen = cell.getRowLength();
932 break;
933 case ROWCOL:
934
935
936
937 bloomKey = generalBloomFilterWriter.createBloomKey(cell.getRowArray(),
938 cell.getRowOffset(), cell.getRowLength(), cell.getQualifierArray(),
939 cell.getQualifierOffset(), cell.getQualifierLength());
940 bloomKeyOffset = 0;
941 bloomKeyLen = bloomKey.length;
942 break;
943 default:
944 throw new IOException("Invalid Bloom filter type: " + bloomType +
945 " (ROW or ROWCOL expected)");
946 }
947 generalBloomFilterWriter.add(bloomKey, bloomKeyOffset, bloomKeyLen);
948 if (lastBloomKey != null
949 && generalBloomFilterWriter.getComparator().compareFlatKey(bloomKey,
950 bloomKeyOffset, bloomKeyLen, lastBloomKey,
951 lastBloomKeyOffset, lastBloomKeyLen) <= 0) {
952 throw new IOException("Non-increasing Bloom keys: "
953 + Bytes.toStringBinary(bloomKey, bloomKeyOffset, bloomKeyLen)
954 + " after "
955 + Bytes.toStringBinary(lastBloomKey, lastBloomKeyOffset,
956 lastBloomKeyLen));
957 }
958 lastBloomKey = bloomKey;
959 lastBloomKeyOffset = bloomKeyOffset;
960 lastBloomKeyLen = bloomKeyLen;
961 this.lastCell = cell;
962 }
963 }
964 }
965
966 private void appendDeleteFamilyBloomFilter(final Cell cell)
967 throws IOException {
968 if (!CellUtil.isDeleteFamily(cell) && !CellUtil.isDeleteFamilyVersion(cell)) {
969 return;
970 }
971
972
973 deleteFamilyCnt++;
974 if (null != this.deleteFamilyBloomFilterWriter) {
975 boolean newKey = true;
976 if (lastDeleteFamilyCell != null) {
977 newKey = !kvComparator.matchingRows(cell, lastDeleteFamilyCell);
978 }
979 if (newKey) {
980 this.deleteFamilyBloomFilterWriter.add(cell.getRowArray(),
981 cell.getRowOffset(), cell.getRowLength());
982 this.lastDeleteFamilyCell = cell;
983 }
984 }
985 }
986
987 public void append(final Cell cell) throws IOException {
988 appendGeneralBloomfilter(cell);
989 appendDeleteFamilyBloomFilter(cell);
990 writer.append(cell);
991 trackTimestamps(cell);
992 }
993
994 public Path getPath() {
995 return this.writer.getPath();
996 }
997
998 public boolean hasGeneralBloom() {
999 return this.generalBloomFilterWriter != null;
1000 }
1001
1002
1003
1004
1005
1006
1007 BloomFilterWriter getGeneralBloomWriter() {
1008 return generalBloomFilterWriter;
1009 }
1010
1011 private boolean closeBloomFilter(BloomFilterWriter bfw) throws IOException {
1012 boolean haveBloom = (bfw != null && bfw.getKeyCount() > 0);
1013 if (haveBloom) {
1014 bfw.compactBloom();
1015 }
1016 return haveBloom;
1017 }
1018
1019 private boolean closeGeneralBloomFilter() throws IOException {
1020 boolean hasGeneralBloom = closeBloomFilter(generalBloomFilterWriter);
1021
1022
1023 if (hasGeneralBloom) {
1024 writer.addGeneralBloomFilter(generalBloomFilterWriter);
1025 writer.appendFileInfo(BLOOM_FILTER_TYPE_KEY,
1026 Bytes.toBytes(bloomType.toString()));
1027 if (lastBloomKey != null) {
1028 writer.appendFileInfo(LAST_BLOOM_KEY, Arrays.copyOfRange(
1029 lastBloomKey, lastBloomKeyOffset, lastBloomKeyOffset
1030 + lastBloomKeyLen));
1031 }
1032 }
1033 return hasGeneralBloom;
1034 }
1035
1036 private boolean closeDeleteFamilyBloomFilter() throws IOException {
1037 boolean hasDeleteFamilyBloom = closeBloomFilter(deleteFamilyBloomFilterWriter);
1038
1039
1040 if (hasDeleteFamilyBloom) {
1041 writer.addDeleteFamilyBloomFilter(deleteFamilyBloomFilterWriter);
1042 }
1043
1044
1045
1046 writer.appendFileInfo(DELETE_FAMILY_COUNT,
1047 Bytes.toBytes(this.deleteFamilyCnt));
1048
1049 return hasDeleteFamilyBloom;
1050 }
1051
1052 public void close() throws IOException {
1053 boolean hasGeneralBloom = this.closeGeneralBloomFilter();
1054 boolean hasDeleteFamilyBloom = this.closeDeleteFamilyBloomFilter();
1055
1056 writer.close();
1057
1058
1059
1060 if (StoreFile.LOG.isTraceEnabled()) {
1061 StoreFile.LOG.trace((hasGeneralBloom ? "" : "NO ") + "General Bloom and " +
1062 (hasDeleteFamilyBloom ? "" : "NO ") + "DeleteFamily" + " was added to HFile " +
1063 getPath());
1064 }
1065
1066 }
1067
1068 public void appendFileInfo(byte[] key, byte[] value) throws IOException {
1069 writer.appendFileInfo(key, value);
1070 }
1071
1072
1073
1074 HFile.Writer getHFileWriter() {
1075 return writer;
1076 }
1077 }
1078
1079
1080
1081
1082 public static class Reader {
1083 private static final Log LOG = LogFactory.getLog(Reader.class.getName());
1084
1085 protected BloomFilter generalBloomFilter = null;
1086 protected BloomFilter deleteFamilyBloomFilter = null;
1087 protected BloomType bloomFilterType;
1088 private final HFile.Reader reader;
1089 protected TimeRangeTracker timeRangeTracker = null;
1090 protected long sequenceID = -1;
1091 private byte[] lastBloomKey;
1092 private long deleteFamilyCnt = -1;
1093 private boolean bulkLoadResult = false;
1094
1095 public Reader(FileSystem fs, Path path, CacheConfig cacheConf, Configuration conf)
1096 throws IOException {
1097 reader = HFile.createReader(fs, path, cacheConf, conf);
1098 bloomFilterType = BloomType.NONE;
1099 }
1100
1101 public Reader(FileSystem fs, Path path, FSDataInputStreamWrapper in, long size,
1102 CacheConfig cacheConf, Configuration conf) throws IOException {
1103 reader = HFile.createReader(fs, path, in, size, cacheConf, conf);
1104 bloomFilterType = BloomType.NONE;
1105 }
1106
1107 public void setReplicaStoreFile(boolean isPrimaryReplicaStoreFile) {
1108 reader.setPrimaryReplicaReader(isPrimaryReplicaStoreFile);
1109 }
1110 public boolean isPrimaryReplicaReader() {
1111 return reader.isPrimaryReplicaReader();
1112 }
1113
1114
1115
1116
1117 Reader() {
1118 this.reader = null;
1119 }
1120
1121 public KVComparator getComparator() {
1122 return reader.getComparator();
1123 }
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133 public StoreFileScanner getStoreFileScanner(boolean cacheBlocks,
1134 boolean pread) {
1135 return getStoreFileScanner(cacheBlocks, pread, false,
1136
1137
1138 0);
1139 }
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149 public StoreFileScanner getStoreFileScanner(boolean cacheBlocks,
1150 boolean pread,
1151 boolean isCompaction, long readPt) {
1152 return new StoreFileScanner(this,
1153 getScanner(cacheBlocks, pread, isCompaction),
1154 !isCompaction, reader.hasMVCCInfo(), readPt);
1155 }
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166 @Deprecated
1167 public HFileScanner getScanner(boolean cacheBlocks, boolean pread) {
1168 return getScanner(cacheBlocks, pread, false);
1169 }
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184 @Deprecated
1185 public HFileScanner getScanner(boolean cacheBlocks, boolean pread,
1186 boolean isCompaction) {
1187 return reader.getScanner(cacheBlocks, pread, isCompaction);
1188 }
1189
1190 public void close(boolean evictOnClose) throws IOException {
1191 reader.close(evictOnClose);
1192 }
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202 boolean passesTimerangeFilter(TimeRange timeRange, long oldestUnexpiredTS) {
1203 if (timeRangeTracker == null) {
1204 return true;
1205 } else {
1206 return timeRangeTracker.includesTimeRange(timeRange) &&
1207 timeRangeTracker.getMaximumTimestamp() >= oldestUnexpiredTS;
1208 }
1209 }
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227 boolean passesBloomFilter(Scan scan,
1228 final SortedSet<byte[]> columns) {
1229
1230
1231 if (!scan.isGetScan()) {
1232 return true;
1233 }
1234
1235 byte[] row = scan.getStartRow();
1236 switch (this.bloomFilterType) {
1237 case ROW:
1238 return passesGeneralBloomFilter(row, 0, row.length, null, 0, 0);
1239
1240 case ROWCOL:
1241 if (columns != null && columns.size() == 1) {
1242 byte[] column = columns.first();
1243 return passesGeneralBloomFilter(row, 0, row.length, column, 0,
1244 column.length);
1245 }
1246
1247
1248
1249 return true;
1250
1251 default:
1252 return true;
1253 }
1254 }
1255
1256 public boolean passesDeleteFamilyBloomFilter(byte[] row, int rowOffset,
1257 int rowLen) {
1258
1259
1260 BloomFilter bloomFilter = this.deleteFamilyBloomFilter;
1261
1262
1263 if (reader.getTrailer().getEntryCount() == 0 || deleteFamilyCnt == 0) {
1264 return false;
1265 }
1266
1267 if (bloomFilter == null) {
1268 return true;
1269 }
1270
1271 try {
1272 if (!bloomFilter.supportsAutoLoading()) {
1273 return true;
1274 }
1275 return bloomFilter.contains(row, rowOffset, rowLen, null);
1276 } catch (IllegalArgumentException e) {
1277 LOG.error("Bad Delete Family bloom filter data -- proceeding without",
1278 e);
1279 setDeleteFamilyBloomFilterFaulty();
1280 }
1281
1282 return true;
1283 }
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297 public boolean passesGeneralBloomFilter(byte[] row, int rowOffset,
1298 int rowLen, byte[] col, int colOffset, int colLen) {
1299
1300
1301 BloomFilter bloomFilter = this.generalBloomFilter;
1302 if (bloomFilter == null) {
1303 return true;
1304 }
1305
1306 byte[] key;
1307 switch (bloomFilterType) {
1308 case ROW:
1309 if (col != null) {
1310 throw new RuntimeException("Row-only Bloom filter called with " +
1311 "column specified");
1312 }
1313 if (rowOffset != 0 || rowLen != row.length) {
1314 throw new AssertionError("For row-only Bloom filters the row "
1315 + "must occupy the whole array");
1316 }
1317 key = row;
1318 break;
1319
1320 case ROWCOL:
1321 key = bloomFilter.createBloomKey(row, rowOffset, rowLen, col,
1322 colOffset, colLen);
1323
1324 break;
1325
1326 default:
1327 return true;
1328 }
1329
1330
1331 if (reader.getTrailer().getEntryCount() == 0)
1332 return false;
1333
1334 try {
1335 boolean shouldCheckBloom;
1336 ByteBuffer bloom;
1337 if (bloomFilter.supportsAutoLoading()) {
1338 bloom = null;
1339 shouldCheckBloom = true;
1340 } else {
1341 bloom = reader.getMetaBlock(HFile.BLOOM_FILTER_DATA_KEY,
1342 true);
1343 shouldCheckBloom = bloom != null;
1344 }
1345
1346 if (shouldCheckBloom) {
1347 boolean exists;
1348
1349
1350
1351
1352 boolean keyIsAfterLast = lastBloomKey != null
1353 && bloomFilter.getComparator().compareFlatKey(key, lastBloomKey) > 0;
1354
1355 if (bloomFilterType == BloomType.ROWCOL) {
1356
1357
1358
1359
1360 byte[] rowBloomKey = bloomFilter.createBloomKey(row, rowOffset, rowLen,
1361 null, 0, 0);
1362
1363 if (keyIsAfterLast
1364 && bloomFilter.getComparator().compareFlatKey(rowBloomKey,
1365 lastBloomKey) > 0) {
1366 exists = false;
1367 } else {
1368 exists =
1369 bloomFilter.contains(key, 0, key.length, bloom) ||
1370 bloomFilter.contains(rowBloomKey, 0, rowBloomKey.length,
1371 bloom);
1372 }
1373 } else {
1374 exists = !keyIsAfterLast
1375 && bloomFilter.contains(key, 0, key.length, bloom);
1376 }
1377
1378 return exists;
1379 }
1380 } catch (IOException e) {
1381 LOG.error("Error reading bloom filter data -- proceeding without",
1382 e);
1383 setGeneralBloomFilterFaulty();
1384 } catch (IllegalArgumentException e) {
1385 LOG.error("Bad bloom filter data -- proceeding without", e);
1386 setGeneralBloomFilterFaulty();
1387 }
1388
1389 return true;
1390 }
1391
1392
1393
1394
1395
1396
1397 public boolean passesKeyRangeFilter(Scan scan) {
1398 if (this.getFirstKey() == null || this.getLastKey() == null) {
1399
1400 return false;
1401 }
1402 if (Bytes.equals(scan.getStartRow(), HConstants.EMPTY_START_ROW)
1403 && Bytes.equals(scan.getStopRow(), HConstants.EMPTY_END_ROW)) {
1404 return true;
1405 }
1406 KeyValue smallestScanKeyValue = scan.isReversed() ? KeyValueUtil
1407 .createFirstOnRow(scan.getStopRow()) : KeyValueUtil.createFirstOnRow(scan
1408 .getStartRow());
1409 KeyValue largestScanKeyValue = scan.isReversed() ? KeyValueUtil
1410 .createLastOnRow(scan.getStartRow()) : KeyValueUtil.createLastOnRow(scan
1411 .getStopRow());
1412 boolean nonOverLapping = (getComparator().compareFlatKey(
1413 this.getFirstKey(), largestScanKeyValue.getKey()) > 0 && !Bytes
1414 .equals(scan.isReversed() ? scan.getStartRow() : scan.getStopRow(),
1415 HConstants.EMPTY_END_ROW))
1416 || getComparator().compareFlatKey(this.getLastKey(),
1417 smallestScanKeyValue.getKey()) < 0;
1418 return !nonOverLapping;
1419 }
1420
1421 public Map<byte[], byte[]> loadFileInfo() throws IOException {
1422 Map<byte [], byte []> fi = reader.loadFileInfo();
1423
1424 byte[] b = fi.get(BLOOM_FILTER_TYPE_KEY);
1425 if (b != null) {
1426 bloomFilterType = BloomType.valueOf(Bytes.toString(b));
1427 }
1428
1429 lastBloomKey = fi.get(LAST_BLOOM_KEY);
1430 byte[] cnt = fi.get(DELETE_FAMILY_COUNT);
1431 if (cnt != null) {
1432 deleteFamilyCnt = Bytes.toLong(cnt);
1433 }
1434
1435 return fi;
1436 }
1437
1438 public void loadBloomfilter() {
1439 this.loadBloomfilter(BlockType.GENERAL_BLOOM_META);
1440 this.loadBloomfilter(BlockType.DELETE_FAMILY_BLOOM_META);
1441 }
1442
1443 private void loadBloomfilter(BlockType blockType) {
1444 try {
1445 if (blockType == BlockType.GENERAL_BLOOM_META) {
1446 if (this.generalBloomFilter != null)
1447 return;
1448
1449 DataInput bloomMeta = reader.getGeneralBloomFilterMetadata();
1450 if (bloomMeta != null) {
1451
1452 if (bloomFilterType == BloomType.NONE) {
1453 throw new IOException(
1454 "valid bloom filter type not found in FileInfo");
1455 } else {
1456 generalBloomFilter = BloomFilterFactory.createFromMeta(bloomMeta,
1457 reader);
1458 if (LOG.isTraceEnabled()) {
1459 LOG.trace("Loaded " + bloomFilterType.toString() + " "
1460 + generalBloomFilter.getClass().getSimpleName()
1461 + " metadata for " + reader.getName());
1462 }
1463 }
1464 }
1465 } else if (blockType == BlockType.DELETE_FAMILY_BLOOM_META) {
1466 if (this.deleteFamilyBloomFilter != null)
1467 return;
1468
1469 DataInput bloomMeta = reader.getDeleteBloomFilterMetadata();
1470 if (bloomMeta != null) {
1471 deleteFamilyBloomFilter = BloomFilterFactory.createFromMeta(
1472 bloomMeta, reader);
1473 LOG.info("Loaded Delete Family Bloom ("
1474 + deleteFamilyBloomFilter.getClass().getSimpleName()
1475 + ") metadata for " + reader.getName());
1476 }
1477 } else {
1478 throw new RuntimeException("Block Type: " + blockType.toString()
1479 + "is not supported for Bloom filter");
1480 }
1481 } catch (IOException e) {
1482 LOG.error("Error reading bloom filter meta for " + blockType
1483 + " -- proceeding without", e);
1484 setBloomFilterFaulty(blockType);
1485 } catch (IllegalArgumentException e) {
1486 LOG.error("Bad bloom filter meta " + blockType
1487 + " -- proceeding without", e);
1488 setBloomFilterFaulty(blockType);
1489 }
1490 }
1491
1492 private void setBloomFilterFaulty(BlockType blockType) {
1493 if (blockType == BlockType.GENERAL_BLOOM_META) {
1494 setGeneralBloomFilterFaulty();
1495 } else if (blockType == BlockType.DELETE_FAMILY_BLOOM_META) {
1496 setDeleteFamilyBloomFilterFaulty();
1497 }
1498 }
1499
1500
1501
1502
1503
1504
1505
1506
1507 public long getFilterEntries() {
1508 return generalBloomFilter != null ? generalBloomFilter.getKeyCount()
1509 : reader.getEntries();
1510 }
1511
1512 public void setGeneralBloomFilterFaulty() {
1513 generalBloomFilter = null;
1514 }
1515
1516 public void setDeleteFamilyBloomFilterFaulty() {
1517 this.deleteFamilyBloomFilter = null;
1518 }
1519
1520 public byte[] getLastKey() {
1521 return reader.getLastKey();
1522 }
1523
1524 public byte[] getLastRowKey() {
1525 return reader.getLastRowKey();
1526 }
1527
1528 public byte[] midkey() throws IOException {
1529 return reader.midkey();
1530 }
1531
1532 public long length() {
1533 return reader.length();
1534 }
1535
1536 public long getTotalUncompressedBytes() {
1537 return reader.getTrailer().getTotalUncompressedBytes();
1538 }
1539
1540 public long getEntries() {
1541 return reader.getEntries();
1542 }
1543
1544 public long getDeleteFamilyCnt() {
1545 return deleteFamilyCnt;
1546 }
1547
1548 public byte[] getFirstKey() {
1549 return reader.getFirstKey();
1550 }
1551
1552 public long indexSize() {
1553 return reader.indexSize();
1554 }
1555
1556 public BloomType getBloomFilterType() {
1557 return this.bloomFilterType;
1558 }
1559
1560 public long getSequenceID() {
1561 return sequenceID;
1562 }
1563
1564 public void setSequenceID(long sequenceID) {
1565 this.sequenceID = sequenceID;
1566 }
1567
1568 public void setBulkLoaded(boolean bulkLoadResult) {
1569 this.bulkLoadResult = bulkLoadResult;
1570 }
1571
1572 public boolean isBulkLoaded() {
1573 return this.bulkLoadResult;
1574 }
1575
1576 BloomFilter getGeneralBloomFilter() {
1577 return generalBloomFilter;
1578 }
1579
1580 long getUncompressedDataIndexSize() {
1581 return reader.getTrailer().getUncompressedDataIndexSize();
1582 }
1583
1584 public long getTotalBloomSize() {
1585 if (generalBloomFilter == null)
1586 return 0;
1587 return generalBloomFilter.getByteSize();
1588 }
1589
1590 public int getHFileVersion() {
1591 return reader.getTrailer().getMajorVersion();
1592 }
1593
1594 public int getHFileMinorVersion() {
1595 return reader.getTrailer().getMinorVersion();
1596 }
1597
1598 public HFile.Reader getHFileReader() {
1599 return reader;
1600 }
1601
1602 void disableBloomFilterForTesting() {
1603 generalBloomFilter = null;
1604 this.deleteFamilyBloomFilter = null;
1605 }
1606
1607 public long getMaxTimestamp() {
1608 return timeRangeTracker == null ? Long.MAX_VALUE : timeRangeTracker.getMaximumTimestamp();
1609 }
1610 }
1611
1612
1613
1614
1615 public abstract static class Comparators {
1616
1617
1618
1619
1620
1621
1622
1623
1624 public static final Comparator<StoreFile> SEQ_ID =
1625 Ordering.compound(ImmutableList.of(
1626 Ordering.natural().onResultOf(new GetSeqId()),
1627 Ordering.natural().onResultOf(new GetFileSize()).reverse(),
1628 Ordering.natural().onResultOf(new GetBulkTime()),
1629 Ordering.natural().onResultOf(new GetPathName())
1630 ));
1631
1632 private static class GetSeqId implements Function<StoreFile, Long> {
1633 @Override
1634 public Long apply(StoreFile sf) {
1635 return sf.getMaxSequenceId();
1636 }
1637 }
1638
1639 private static class GetFileSize implements Function<StoreFile, Long> {
1640 @Override
1641 public Long apply(StoreFile sf) {
1642 return sf.getReader().length();
1643 }
1644 }
1645
1646 private static class GetBulkTime implements Function<StoreFile, Long> {
1647 @Override
1648 public Long apply(StoreFile sf) {
1649 if (!sf.isBulkLoadResult()) return Long.MAX_VALUE;
1650 return sf.getBulkLoadTimestamp();
1651 }
1652 }
1653
1654 private static class GetPathName implements Function<StoreFile, String> {
1655 @Override
1656 public String apply(StoreFile sf) {
1657 return sf.getPath().getName();
1658 }
1659 }
1660 }
1661 }