001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.hbase.regionserver;
019
020import static org.apache.hadoop.hbase.io.crypto.ManagedKeyData.KEY_SPACE_GLOBAL;
021
022import java.io.IOException;
023import java.io.UnsupportedEncodingException;
024import java.net.URLEncoder;
025import java.util.Collection;
026import java.util.Collections;
027import java.util.HashSet;
028import java.util.Map;
029import java.util.Optional;
030import java.util.OptionalLong;
031import java.util.Set;
032import java.util.concurrent.atomic.AtomicBoolean;
033import org.apache.hadoop.conf.Configuration;
034import org.apache.hadoop.fs.FSDataInputStream;
035import org.apache.hadoop.fs.FileSystem;
036import org.apache.hadoop.fs.Path;
037import org.apache.hadoop.hbase.CellComparator;
038import org.apache.hadoop.hbase.ExtendedCell;
039import org.apache.hadoop.hbase.HConstants;
040import org.apache.hadoop.hbase.HDFSBlocksDistribution;
041import org.apache.hadoop.hbase.io.TimeRange;
042import org.apache.hadoop.hbase.io.hfile.BlockType;
043import org.apache.hadoop.hbase.io.hfile.BloomFilterMetrics;
044import org.apache.hadoop.hbase.io.hfile.CacheConfig;
045import org.apache.hadoop.hbase.io.hfile.HFile;
046import org.apache.hadoop.hbase.io.hfile.ReaderContext;
047import org.apache.hadoop.hbase.io.hfile.ReaderContext.ReaderType;
048import org.apache.hadoop.hbase.keymeta.ManagedKeyDataCache;
049import org.apache.hadoop.hbase.keymeta.SystemKeyCache;
050import org.apache.hadoop.hbase.regionserver.storefiletracker.StoreFileTracker;
051import org.apache.hadoop.hbase.util.BloomFilterFactory;
052import org.apache.hadoop.hbase.util.Bytes;
053import org.apache.yetus.audience.InterfaceAudience;
054import org.slf4j.Logger;
055import org.slf4j.LoggerFactory;
056
057import org.apache.hbase.thirdparty.com.google.common.base.Preconditions;
058import org.apache.hbase.thirdparty.org.apache.commons.collections4.CollectionUtils;
059
060import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil;
061
062/**
063 * A Store data file. Stores usually have one or more of these files. They are produced by flushing
064 * the memstore to disk. To create, instantiate a writer using {@link StoreFileWriter.Builder} and
065 * append data. Be sure to add any metadata before calling close on the Writer (Use the
066 * appendMetadata convenience methods). On close, a StoreFile is sitting in the Filesystem. To refer
067 * to it, create a StoreFile instance passing filesystem and path. To read, call
068 * {@link #initReader()}
069 * <p>
070 * StoreFiles may also reference store files in another Store. The reason for this weird pattern
071 * where you use a different instance for the writer and a reader is that we write once but read a
072 * lot more.
073 */
074@InterfaceAudience.Private
075public class HStoreFile implements StoreFile {
076
077  private static final Logger LOG = LoggerFactory.getLogger(HStoreFile.class.getName());
078
079  // Keys for fileinfo values in HFile
080
081  /** Max Sequence ID in FileInfo */
082  public static final byte[] MAX_SEQ_ID_KEY = Bytes.toBytes("MAX_SEQ_ID_KEY");
083
084  /** Major compaction flag in FileInfo */
085  public static final byte[] MAJOR_COMPACTION_KEY = Bytes.toBytes("MAJOR_COMPACTION_KEY");
086
087  /** Minor compaction flag in FileInfo */
088  public static final byte[] EXCLUDE_FROM_MINOR_COMPACTION_KEY =
089    Bytes.toBytes("EXCLUDE_FROM_MINOR_COMPACTION");
090
091  /**
092   * Key for compaction event which contains the compacted storefiles in FileInfo
093   */
094  public static final byte[] COMPACTION_EVENT_KEY = Bytes.toBytes("COMPACTION_EVENT_KEY");
095
096  /** Bloom filter Type in FileInfo */
097  public static final byte[] BLOOM_FILTER_TYPE_KEY = Bytes.toBytes("BLOOM_FILTER_TYPE");
098
099  /** Bloom filter param in FileInfo */
100  public static final byte[] BLOOM_FILTER_PARAM_KEY = Bytes.toBytes("BLOOM_FILTER_PARAM");
101
102  /** Delete Family Count in FileInfo */
103  public static final byte[] DELETE_FAMILY_COUNT = Bytes.toBytes("DELETE_FAMILY_COUNT");
104
105  /** Last Bloom filter key in FileInfo */
106  public static final byte[] LAST_BLOOM_KEY = Bytes.toBytes("LAST_BLOOM_KEY");
107
108  /** Key for Timerange information in metadata */
109  public static final byte[] TIMERANGE_KEY = Bytes.toBytes("TIMERANGE");
110
111  /** Key for timestamp of earliest-put in metadata */
112  public static final byte[] EARLIEST_PUT_TS = Bytes.toBytes("EARLIEST_PUT_TS");
113
114  /** Key for the number of mob cells in metadata */
115  public static final byte[] MOB_CELLS_COUNT = Bytes.toBytes("MOB_CELLS_COUNT");
116
117  /** Null data */
118  public static final byte[] NULL_VALUE = new byte[] { 0 };
119
120  /** Key for the list of MOB file references */
121  public static final byte[] MOB_FILE_REFS = Bytes.toBytes("MOB_FILE_REFS");
122
123  /** Meta key set when store file is a result of a bulk load */
124  public static final byte[] BULKLOAD_TASK_KEY = Bytes.toBytes("BULKLOAD_SOURCE_TASK");
125  public static final byte[] BULKLOAD_TIME_KEY = Bytes.toBytes("BULKLOAD_TIMESTAMP");
126
127  /**
128   * Key for skipping resetting sequence id in metadata. For bulk loaded hfiles, the scanner resets
129   * the cell seqId with the latest one, if this metadata is set as true, the reset is skipped.
130   */
131  public static final byte[] SKIP_RESET_SEQ_ID = Bytes.toBytes("SKIP_RESET_SEQ_ID");
132
133  public static final byte[] HISTORICAL_KEY = Bytes.toBytes("HISTORICAL");
134
135  private final StoreFileInfo fileInfo;
136
137  // StoreFile.Reader
138  private volatile StoreFileReader initialReader;
139  private volatile InputStreamBlockDistribution initialReaderBlockDistribution = null;
140
141  // Block cache configuration and reference.
142  private final CacheConfig cacheConf;
143  private final BloomFilterMetrics metrics;
144
145  // Indicates if the file got compacted
146  private volatile boolean compactedAway = false;
147
148  // Indicates if the file contains historical cell versions. This is used when
149  // hbase.enable.historical.compaction.files is set to true. In that case, compactions
150  // can generate two files, one with the live cell versions and the other with the remaining
151  // (historical) cell versions. If isHistorical is true then the hfile is historical.
152  // Historical files are skipped for regular (not raw) scans for latest row versions.
153  // When hbase.enable.historical.compaction.files is false, isHistorical will be false
154  // for all files. This means all files will be treated as live files. Historical files are
155  // generated only when hbase.enable.historical.compaction.files is true.
156  private volatile boolean isHistorical = false;
157
158  // Keys for metadata stored in backing HFile.
159  // Set when we obtain a Reader.
160  private long sequenceid = -1;
161
162  // max of the MemstoreTS in the KV's in this store
163  // Set when we obtain a Reader.
164  private long maxMemstoreTS = -1;
165
166  // firstKey, lastkey and cellComparator will be set when openReader.
167  private Optional<ExtendedCell> firstKey;
168
169  private Optional<ExtendedCell> lastKey;
170
171  private CellComparator comparator;
172
173  public CacheConfig getCacheConf() {
174    return this.cacheConf;
175  }
176
177  @Override
178  public Optional<ExtendedCell> getFirstKey() {
179    return firstKey;
180  }
181
182  @Override
183  public Optional<ExtendedCell> getLastKey() {
184    return lastKey;
185  }
186
187  @Override
188  public CellComparator getComparator() {
189    return comparator;
190  }
191
192  @Override
193  public long getMaxMemStoreTS() {
194    return maxMemstoreTS;
195  }
196
197  // If true, this file was product of a major compaction. Its then set
198  // whenever you get a Reader.
199  private AtomicBoolean majorCompaction = null;
200
201  // If true, this file should not be included in minor compactions.
202  // It's set whenever you get a Reader.
203  private boolean excludeFromMinorCompaction = false;
204
205  // This file was product of these compacted store files
206  private final Set<String> compactedStoreFiles = new HashSet<>();
207
208  /**
209   * Map of the metadata entries in the corresponding HFile. Populated when Reader is opened after
210   * which it is not modified again.
211   */
212  private Map<byte[], byte[]> metadataMap;
213
214  /**
215   * Bloom filter type specified in column family configuration. Does not necessarily correspond to
216   * the Bloom filter type present in the HFile.
217   */
218  private final BloomType cfBloomType;
219
220  private String keyNamespace;
221
222  private SystemKeyCache systemKeyCache;
223
224  private final ManagedKeyDataCache managedKeyDataCache;
225
226  /**
227   * Constructor, loads a reader and it's indices, etc. May allocate a substantial amount of ram
228   * depending on the underlying files (10-20MB?). Since this is used only in read path, key
229   * namespace is not needed.
230   * @param fs             The current file system to use.
231   * @param p              The path of the file.
232   * @param conf           The current configuration.
233   * @param cacheConf      The cache configuration and block cache reference.
234   * @param cfBloomType    The bloom type to use for this store file as specified by column family
235   *                       configuration. This may or may not be the same as the Bloom filter type
236   *                       actually present in the HFile, because column family configuration might
237   *                       change. If this is {@link BloomType#NONE}, the existing Bloom filter is
238   *                       ignored.
239   * @param primaryReplica true if this is a store file for primary replica, otherwise false.
240   */
241  public HStoreFile(FileSystem fs, Path p, Configuration conf, CacheConfig cacheConf,
242    BloomType cfBloomType, boolean primaryReplica, StoreFileTracker sft) throws IOException {
243    // Key management not yet implemented - always null
244    this(sft.getStoreFileInfo(p, primaryReplica), cfBloomType, cacheConf, null, null, null, null);
245  }
246
247  /**
248   * Constructor, loads a reader and it's indices, etc. May allocate a substantial amount of ram
249   * depending on the underlying files (10-20MB?).
250   * @param fileInfo    The store file information.
251   * @param cfBloomType The bloom type to use for this store file as specified by column family
252   *                    configuration. This may or may not be the same as the Bloom filter type
253   *                    actually present in the HFile, because column family configuration might
254   *                    change. If this is {@link BloomType#NONE}, the existing Bloom filter is
255   *                    ignored.
256   * @param cacheConf   The cache configuration and block cache reference.
257   */
258  public HStoreFile(StoreFileInfo fileInfo, BloomType cfBloomType, CacheConfig cacheConf)
259    throws IOException {
260    // Key management not yet implemented - always null
261    this(fileInfo, cfBloomType, cacheConf, null, null, // keyNamespace - not yet implemented
262      null, null);
263  }
264
265  /**
266   * Constructor, loads a reader and it's indices, etc. May allocate a substantial amount of ram
267   * depending on the underlying files (10-20MB?).
268   * @param fileInfo    The store file information.
269   * @param cfBloomType The bloom type to use for this store file as specified by column family
270   *                    configuration. This may or may not be the same as the Bloom filter type
271   *                    actually present in the HFile, because column family configuration might
272   *                    change. If this is {@link BloomType#NONE}, the existing Bloom filter is
273   *                    ignored.
274   * @param cacheConf   The cache configuration and block cache reference.
275   * @param metrics     Tracks bloom filter requests and results. May be null.
276   */
277  public HStoreFile(StoreFileInfo fileInfo, BloomType cfBloomType, CacheConfig cacheConf,
278    BloomFilterMetrics metrics, String keyNamespace, SystemKeyCache systemKeyCache,
279    ManagedKeyDataCache managedKeyDataCache) {
280    this.fileInfo = fileInfo;
281    this.cacheConf = cacheConf;
282    this.metrics = metrics;
283    this.keyNamespace = keyNamespace != null ? keyNamespace : KEY_SPACE_GLOBAL;
284    this.systemKeyCache = systemKeyCache;
285    this.managedKeyDataCache = managedKeyDataCache;
286    if (BloomFilterFactory.isGeneralBloomEnabled(fileInfo.getConf())) {
287      this.cfBloomType = cfBloomType;
288    } else {
289      LOG.info("Ignoring bloom filter check for file " + this.getPath() + ": " + "cfBloomType="
290        + cfBloomType + " (disabled in config)");
291      this.cfBloomType = BloomType.NONE;
292    }
293  }
294
295  /**
296   * @return the StoreFile object associated to this StoreFile. null if the StoreFile is not a
297   *         reference.
298   */
299  public StoreFileInfo getFileInfo() {
300    return this.fileInfo;
301  }
302
303  @Override
304  public Path getPath() {
305    return this.fileInfo.getPath();
306  }
307
308  @Override
309  public Path getEncodedPath() {
310    try {
311      return new Path(URLEncoder.encode(fileInfo.getPath().toString(), HConstants.UTF8_ENCODING));
312    } catch (UnsupportedEncodingException ex) {
313      throw new RuntimeException("URLEncoder doesn't support UTF-8", ex);
314    }
315  }
316
317  @Override
318  public Path getQualifiedPath() {
319    FileSystem fs = fileInfo.getFileSystem();
320    return this.fileInfo.getPath().makeQualified(fs.getUri(), fs.getWorkingDirectory());
321  }
322
323  @Override
324  public boolean isReference() {
325    return this.fileInfo.isReference();
326  }
327
328  @Override
329  public boolean isHFile() {
330    return StoreFileInfo.isHFile(this.fileInfo.getPath());
331  }
332
333  @Override
334  public boolean isMajorCompactionResult() {
335    Preconditions.checkState(this.majorCompaction != null, "Major compation has not been set yet");
336    return this.majorCompaction.get();
337  }
338
339  @Override
340  public boolean excludeFromMinorCompaction() {
341    return this.excludeFromMinorCompaction;
342  }
343
344  @Override
345  public long getMaxSequenceId() {
346    return this.sequenceid;
347  }
348
349  @Override
350  public long getModificationTimestamp() throws IOException {
351    return fileInfo.getModificationTime();
352  }
353
354  /**
355   * @param key to look up
356   * @return value associated with the metadata key
357   */
358  public byte[] getMetadataValue(byte[] key) {
359    return metadataMap.get(key);
360  }
361
362  @Override
363  public boolean isBulkLoadResult() {
364    return StoreFileInfo.hasBulkloadSeqId(this.getPath())
365      || (metadataMap != null && metadataMap.containsKey(BULKLOAD_TIME_KEY));
366  }
367
368  public boolean isCompactedAway() {
369    return compactedAway;
370  }
371
372  public boolean isHistorical() {
373    return isHistorical;
374  }
375
376  public int getRefCount() {
377    return fileInfo.getRefCount();
378  }
379
380  /** Returns true if the file is still used in reads */
381  public boolean isReferencedInReads() {
382    int rc = fileInfo.getRefCount();
383    assert rc >= 0; // we should not go negative.
384    return rc > 0;
385  }
386
387  @Override
388  public OptionalLong getBulkLoadTimestamp() {
389    byte[] bulkLoadTimestamp = metadataMap.get(BULKLOAD_TIME_KEY);
390    return bulkLoadTimestamp == null
391      ? OptionalLong.empty()
392      : OptionalLong.of(Bytes.toLong(bulkLoadTimestamp));
393  }
394
395  /**
396   * @return the cached value of HDFS blocks distribution. The cached value is calculated when store
397   *         file is opened.
398   */
399  public HDFSBlocksDistribution getHDFSBlockDistribution() {
400    if (initialReaderBlockDistribution != null) {
401      return initialReaderBlockDistribution.getHDFSBlockDistribution();
402    } else {
403      return this.fileInfo.getHDFSBlockDistribution();
404    }
405  }
406
407  /**
408   * Opens reader on this store file. Called by Constructor.
409   * @see #closeStoreFile(boolean)
410   */
411  private void open() throws IOException {
412    fileInfo.initHDFSBlocksDistribution();
413    long readahead = fileInfo.isNoReadahead() ? 0L : -1L;
414    ReaderContext context = fileInfo.createReaderContext(false, readahead, ReaderType.PREAD,
415      keyNamespace, systemKeyCache, managedKeyDataCache);
416    fileInfo.initHFileInfo(context);
417    StoreFileReader reader = fileInfo.preStoreFileReaderOpen(context, cacheConf);
418    if (reader == null) {
419      reader = fileInfo.createReader(context, cacheConf);
420      fileInfo.getHFileInfo().initMetaAndIndex(reader.getHFileReader());
421    }
422    this.initialReader = fileInfo.postStoreFileReaderOpen(context, cacheConf, reader);
423
424    if (InputStreamBlockDistribution.isEnabled(fileInfo.getConf())) {
425      boolean useHBaseChecksum = context.getInputStreamWrapper().shouldUseHBaseChecksum();
426      FSDataInputStream stream = context.getInputStreamWrapper().getStream(useHBaseChecksum);
427      this.initialReaderBlockDistribution = new InputStreamBlockDistribution(stream, fileInfo);
428    }
429
430    // Load up indices and fileinfo. This also loads Bloom filter type.
431    metadataMap = Collections.unmodifiableMap(initialReader.loadFileInfo());
432
433    // Read in our metadata.
434    byte[] b = metadataMap.get(MAX_SEQ_ID_KEY);
435    if (b != null) {
436      // By convention, if halfhfile, top half has a sequence number > bottom
437      // half. Thats why we add one in below. Its done for case the two halves
438      // are ever merged back together --rare. Without it, on open of store,
439      // since store files are distinguished by sequence id, the one half would
440      // subsume the other.
441      this.sequenceid = Bytes.toLong(b);
442      if (fileInfo.isTopReference()) {
443        this.sequenceid += 1;
444      }
445    }
446
447    if (isBulkLoadResult()) {
448      // For bulkloads, we have to parse the sequenceid from the path name
449      OptionalLong sequenceId = StoreFileInfo.getBulkloadSeqId(this.getPath());
450      if (sequenceId.isPresent()) {
451        this.sequenceid = sequenceId.getAsLong();
452        // Handle reference files as done above.
453        if (fileInfo.isTopReference()) {
454          this.sequenceid += 1;
455        }
456      }
457
458      // SKIP_RESET_SEQ_ID only works in bulk loaded file.
459      // In mob compaction, the hfile where the cells contain the path of a new mob file is bulk
460      // loaded to hbase, these cells have the same seqIds with the old ones. We do not want
461      // to reset new seqIds for them since this might make a mess of the visibility of cells that
462      // have the same row key but different seqIds.
463      boolean skipResetSeqId = isSkipResetSeqId(metadataMap.get(SKIP_RESET_SEQ_ID));
464      if (skipResetSeqId) {
465        // increase the seqId when it is a bulk loaded file from mob compaction.
466        this.sequenceid += 1;
467      }
468      initialReader.setSkipResetSeqId(skipResetSeqId);
469      initialReader.setBulkLoaded(true);
470    }
471    initialReader.setSequenceID(this.sequenceid);
472
473    b = metadataMap.get(HFile.Writer.MAX_MEMSTORE_TS_KEY);
474    if (b != null) {
475      this.maxMemstoreTS = Bytes.toLong(b);
476    }
477
478    b = metadataMap.get(MAJOR_COMPACTION_KEY);
479    if (b != null) {
480      boolean mc = Bytes.toBoolean(b);
481      if (this.majorCompaction == null) {
482        this.majorCompaction = new AtomicBoolean(mc);
483      } else {
484        this.majorCompaction.set(mc);
485      }
486    } else {
487      // Presume it is not major compacted if it doesn't explicity say so
488      // HFileOutputFormat explicitly sets the major compacted key.
489      this.majorCompaction = new AtomicBoolean(false);
490    }
491
492    b = metadataMap.get(EXCLUDE_FROM_MINOR_COMPACTION_KEY);
493    this.excludeFromMinorCompaction = (b != null && Bytes.toBoolean(b));
494
495    b = metadataMap.get(HISTORICAL_KEY);
496    if (b != null) {
497      isHistorical = Bytes.toBoolean(b);
498    }
499    BloomType hfileBloomType = initialReader.getBloomFilterType();
500    if (cfBloomType != BloomType.NONE) {
501      initialReader.loadBloomfilter(BlockType.GENERAL_BLOOM_META, metrics);
502      if (hfileBloomType != cfBloomType) {
503        LOG.debug("HFile Bloom filter type for " + initialReader.getHFileReader().getName() + ": "
504          + hfileBloomType + ", but " + cfBloomType + " specified in column family "
505          + "configuration");
506      }
507    } else if (hfileBloomType != BloomType.NONE) {
508      LOG.info(
509        "Bloom filter turned off by CF config for " + initialReader.getHFileReader().getName());
510    }
511
512    // load delete family bloom filter
513    initialReader.loadBloomfilter(BlockType.DELETE_FAMILY_BLOOM_META, metrics);
514
515    try {
516      byte[] data = metadataMap.get(TIMERANGE_KEY);
517      initialReader.timeRange =
518        data == null ? null : TimeRangeTracker.parseFrom(data).toTimeRange();
519    } catch (IllegalArgumentException e) {
520      LOG.error("Error reading timestamp range data from meta -- " + "proceeding without", e);
521      this.initialReader.timeRange = null;
522    }
523
524    try {
525      byte[] data = metadataMap.get(COMPACTION_EVENT_KEY);
526      this.compactedStoreFiles.addAll(ProtobufUtil.toCompactedStoreFiles(data));
527    } catch (IOException e) {
528      LOG.error("Error reading compacted storefiles from meta data", e);
529    }
530
531    // initialize so we can reuse them after reader closed.
532    firstKey = initialReader.getFirstKey();
533    lastKey = initialReader.getLastKey();
534    comparator = initialReader.getComparator();
535  }
536
537  /**
538   * Initialize the reader used for pread.
539   */
540  public void initReader() throws IOException {
541    if (initialReader == null) {
542      synchronized (this) {
543        if (initialReader == null) {
544          try {
545            open();
546          } catch (Exception e) {
547            try {
548              boolean evictOnClose = cacheConf != null ? cacheConf.shouldEvictOnClose() : true;
549              this.closeStoreFile(evictOnClose);
550            } catch (IOException ee) {
551              LOG.warn("failed to close reader", ee);
552            }
553            throw e;
554          }
555        }
556      }
557    }
558  }
559
560  private StoreFileReader createStreamReader(boolean canUseDropBehind) throws IOException {
561    initReader();
562    final boolean doDropBehind = canUseDropBehind && cacheConf.shouldDropBehindCompaction();
563    ReaderContext context = fileInfo.createReaderContext(doDropBehind, -1, ReaderType.STREAM,
564      keyNamespace, systemKeyCache, managedKeyDataCache);
565    StoreFileReader reader = fileInfo.preStoreFileReaderOpen(context, cacheConf);
566    if (reader == null) {
567      reader = fileInfo.createReader(context, cacheConf);
568      // steam reader need copy stuffs from pread reader
569      reader.copyFields(initialReader);
570    }
571    return fileInfo.postStoreFileReaderOpen(context, cacheConf, reader);
572  }
573
574  /**
575   * Get a scanner which uses pread.
576   * <p>
577   * Must be called after initReader.
578   */
579  public StoreFileScanner getPreadScanner(boolean cacheBlocks, long readPt, long scannerOrder,
580    boolean canOptimizeForNonNullColumn) {
581    return getReader().getStoreFileScanner(cacheBlocks, true, false, readPt, scannerOrder,
582      canOptimizeForNonNullColumn);
583  }
584
585  /**
586   * Get a scanner which uses streaming read.
587   * <p>
588   * Must be called after initReader.
589   */
590  public StoreFileScanner getStreamScanner(boolean canUseDropBehind, boolean cacheBlocks,
591    boolean isCompaction, long readPt, long scannerOrder, boolean canOptimizeForNonNullColumn)
592    throws IOException {
593    return createStreamReader(canUseDropBehind).getStoreFileScanner(cacheBlocks, false,
594      isCompaction, readPt, scannerOrder, canOptimizeForNonNullColumn);
595  }
596
597  /**
598   * @return Current reader. Must call initReader first else returns null.
599   * @see #initReader()
600   */
601  public StoreFileReader getReader() {
602    return this.initialReader;
603  }
604
605  /**
606   * @param evictOnClose whether to evict blocks belonging to this file
607   */
608  public synchronized void closeStoreFile(boolean evictOnClose) throws IOException {
609    if (this.initialReader != null) {
610      this.initialReader.close(evictOnClose);
611      this.initialReader = null;
612    }
613  }
614
615  /**
616   * Delete this file
617   */
618  public void deleteStoreFile() throws IOException {
619    boolean evictOnClose = cacheConf != null ? cacheConf.shouldEvictOnClose() : true;
620    closeStoreFile(evictOnClose);
621    this.fileInfo.getFileSystem().delete(getPath(), true);
622  }
623
624  public void markCompactedAway() {
625    this.compactedAway = true;
626  }
627
628  @Override
629  public String toString() {
630    return this.fileInfo.toString();
631  }
632
633  @Override
634  public String toStringDetailed() {
635    StringBuilder sb = new StringBuilder();
636    sb.append(this.getPath().toString());
637    sb.append(", isReference=").append(isReference());
638    sb.append(", isBulkLoadResult=").append(isBulkLoadResult());
639    if (isBulkLoadResult()) {
640      sb.append(", bulkLoadTS=");
641      OptionalLong bulkLoadTS = getBulkLoadTimestamp();
642      if (bulkLoadTS.isPresent()) {
643        sb.append(bulkLoadTS.getAsLong());
644      } else {
645        sb.append("NotPresent");
646      }
647    } else {
648      sb.append(", seqid=").append(getMaxSequenceId());
649    }
650    sb.append(", majorCompaction=").append(isMajorCompactionResult());
651
652    return sb.toString();
653  }
654
655  /**
656   * Gets whether to skip resetting the sequence id for cells.
657   * @param skipResetSeqId The byte array of boolean.
658   * @return Whether to skip resetting the sequence id.
659   */
660  private boolean isSkipResetSeqId(byte[] skipResetSeqId) {
661    if (skipResetSeqId != null && skipResetSeqId.length == 1) {
662      return Bytes.toBoolean(skipResetSeqId);
663    }
664    return false;
665  }
666
667  @Override
668  public OptionalLong getMinimumTimestamp() {
669    TimeRange tr = getReader().timeRange;
670    return tr != null ? OptionalLong.of(tr.getMin()) : OptionalLong.empty();
671  }
672
673  @Override
674  public OptionalLong getMaximumTimestamp() {
675    TimeRange tr = getReader().timeRange;
676    return tr != null ? OptionalLong.of(tr.getMax()) : OptionalLong.empty();
677  }
678
679  Set<String> getCompactedStoreFiles() {
680    return Collections.unmodifiableSet(this.compactedStoreFiles);
681  }
682
683  long increaseRefCount() {
684    return this.fileInfo.increaseRefCount();
685  }
686
687  long decreaseRefCount() {
688    return this.fileInfo.decreaseRefCount();
689  }
690
691  static void increaseStoreFilesRefeCount(Collection<HStoreFile> storeFiles) {
692    if (CollectionUtils.isEmpty(storeFiles)) {
693      return;
694    }
695    storeFiles.forEach(HStoreFile::increaseRefCount);
696  }
697
698  static void decreaseStoreFilesRefeCount(Collection<HStoreFile> storeFiles) {
699    if (CollectionUtils.isEmpty(storeFiles)) {
700      return;
701    }
702    storeFiles.forEach(HStoreFile::decreaseRefCount);
703  }
704}