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