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 java.io.FileNotFoundException;
021import java.io.IOException;
022import java.util.OptionalLong;
023import java.util.concurrent.atomic.AtomicInteger;
024import java.util.regex.Matcher;
025import java.util.regex.Pattern;
026import org.apache.hadoop.conf.Configurable;
027import org.apache.hadoop.conf.Configuration;
028import org.apache.hadoop.fs.FileStatus;
029import org.apache.hadoop.fs.FileSystem;
030import org.apache.hadoop.fs.Path;
031import org.apache.hadoop.hbase.HDFSBlocksDistribution;
032import org.apache.hadoop.hbase.io.FSDataInputStreamWrapper;
033import org.apache.hadoop.hbase.io.HFileLink;
034import org.apache.hadoop.hbase.io.HalfStoreFileReader;
035import org.apache.hadoop.hbase.io.Reference;
036import org.apache.hadoop.hbase.io.hfile.CacheConfig;
037import org.apache.hadoop.hbase.io.hfile.HFileInfo;
038import org.apache.hadoop.hbase.io.hfile.ReaderContext;
039import org.apache.hadoop.hbase.io.hfile.ReaderContext.ReaderType;
040import org.apache.hadoop.hbase.io.hfile.ReaderContextBuilder;
041import org.apache.hadoop.hbase.mob.MobUtils;
042import org.apache.hadoop.hbase.util.FSUtils;
043import org.apache.hadoop.hbase.util.Pair;
044import org.apache.yetus.audience.InterfaceAudience;
045import org.slf4j.Logger;
046import org.slf4j.LoggerFactory;
047
048/**
049 * Describe a StoreFile (hfile, reference, link)
050 */
051@InterfaceAudience.Private
052public class StoreFileInfo implements Configurable {
053  private static final Logger LOG = LoggerFactory.getLogger(StoreFileInfo.class);
054
055  /**
056   * A non-capture group, for hfiles, so that this can be embedded. HFiles are uuid ([0-9a-z]+).
057   * Bulk loaded hfiles has (_SeqId_[0-9]+_) has suffix. The mob del file has (_del) as suffix.
058   */
059  public static final String HFILE_NAME_REGEX = "[0-9a-f]+(?:(?:_SeqId_[0-9]+_)|(?:_del))?";
060
061  /** Regex that will work for hfiles */
062  private static final Pattern HFILE_NAME_PATTERN = Pattern.compile("^(" + HFILE_NAME_REGEX + ")");
063
064  /**
065   * Regex that will work for straight reference names ({@code <hfile>.<parentEncRegion>}) and
066   * hfilelink reference names ({@code
067   *
068  <table>
069   * =<region>-<hfile>.<parentEncRegion>}) If reference, then the regex has more than just one
070   * group. Group 1, hfile/hfilelink pattern, is this file's id. Group 2 '(.+)' is the reference's
071   * parent region name.
072   */
073  private static final Pattern REF_NAME_PATTERN =
074    Pattern.compile(String.format("^(%s|%s)\\.(.+)$", HFILE_NAME_REGEX, HFileLink.LINK_NAME_REGEX));
075
076  public static final String STORE_FILE_READER_NO_READAHEAD = "hbase.store.reader.no-readahead";
077  public static final boolean DEFAULT_STORE_FILE_READER_NO_READAHEAD = true;
078
079  // Configuration
080  private Configuration conf;
081
082  // FileSystem handle
083  private final FileSystem fs;
084
085  // HDFS blocks distribution information
086  private HDFSBlocksDistribution hdfsBlocksDistribution = null;
087
088  private HFileInfo hfileInfo;
089
090  // If this storefile references another, this is the reference instance.
091  private final Reference reference;
092
093  // If this storefile is a link to another, this is the link instance.
094  private final HFileLink link;
095
096  private final Path initialPath;
097
098  private RegionCoprocessorHost coprocessorHost;
099
100  // timestamp on when the file was created, is 0 and ignored for reference or link files
101  private long createdTimestamp;
102
103  private long size;
104
105  private final boolean primaryReplica;
106
107  private final boolean noReadahead;
108
109  // Counter that is incremented every time a scanner is created on the
110  // store file. It is decremented when the scan on the store file is
111  // done.
112  private final AtomicInteger refCount = new AtomicInteger(0);
113
114  /**
115   * Create a Store File Info
116   * @param conf           the {@link Configuration} to use
117   * @param fs             The current file system to use.
118   * @param initialPath    The {@link Path} of the file
119   * @param primaryReplica true if this is a store file for primary replica, otherwise false.
120   */
121  public StoreFileInfo(final Configuration conf, final FileSystem fs, final Path initialPath,
122    final boolean primaryReplica) throws IOException {
123    this(conf, fs, null, initialPath, primaryReplica);
124  }
125
126  private StoreFileInfo(final Configuration conf, final FileSystem fs, final FileStatus fileStatus,
127    final Path initialPath, final boolean primaryReplica) throws IOException {
128    assert fs != null;
129    assert initialPath != null;
130    assert conf != null;
131
132    this.fs = fs;
133    this.conf = conf;
134    this.initialPath = fs.makeQualified(initialPath);
135    this.primaryReplica = primaryReplica;
136    this.noReadahead =
137      this.conf.getBoolean(STORE_FILE_READER_NO_READAHEAD, DEFAULT_STORE_FILE_READER_NO_READAHEAD);
138    Path p = initialPath;
139    if (HFileLink.isHFileLink(p)) {
140      // HFileLink
141      this.reference = null;
142      this.link = HFileLink.buildFromHFileLinkPattern(conf, p);
143      LOG.trace("{} is a link", p);
144    } else if (isReference(p)) {
145      this.reference = Reference.read(fs, p);
146      Path referencePath = getReferredToFile(p);
147      if (HFileLink.isHFileLink(referencePath)) {
148        // HFileLink Reference
149        this.link = HFileLink.buildFromHFileLinkPattern(conf, referencePath);
150      } else {
151        // Reference
152        this.link = null;
153      }
154      LOG.trace("{} is a {} reference to {}", p, reference.getFileRegion(), referencePath);
155    } else if (isHFile(p) || isMobFile(p) || isMobRefFile(p)) {
156      // HFile
157      if (fileStatus != null) {
158        this.createdTimestamp = fileStatus.getModificationTime();
159        this.size = fileStatus.getLen();
160      } else {
161        FileStatus fStatus = fs.getFileStatus(initialPath);
162        this.createdTimestamp = fStatus.getModificationTime();
163        this.size = fStatus.getLen();
164      }
165      this.reference = null;
166      this.link = null;
167    } else {
168      throw new IOException("path=" + p + " doesn't look like a valid StoreFile");
169    }
170  }
171
172  /**
173   * Create a Store File Info
174   * @param conf       the {@link Configuration} to use
175   * @param fs         The current file system to use.
176   * @param fileStatus The {@link FileStatus} of the file
177   */
178  public StoreFileInfo(final Configuration conf, final FileSystem fs, final FileStatus fileStatus)
179    throws IOException {
180    this(conf, fs, fileStatus, fileStatus.getPath(), true);
181  }
182
183  /**
184   * Create a Store File Info from an HFileLink
185   * @param conf       The {@link Configuration} to use
186   * @param fs         The current file system to use
187   * @param fileStatus The {@link FileStatus} of the file
188   */
189  public StoreFileInfo(final Configuration conf, final FileSystem fs, final FileStatus fileStatus,
190    final HFileLink link) {
191    this(conf, fs, fileStatus, null, link);
192  }
193
194  /**
195   * Create a Store File Info from an HFileLink
196   * @param conf       The {@link Configuration} to use
197   * @param fs         The current file system to use
198   * @param fileStatus The {@link FileStatus} of the file
199   * @param reference  The reference instance
200   */
201  public StoreFileInfo(final Configuration conf, final FileSystem fs, final FileStatus fileStatus,
202    final Reference reference) {
203    this(conf, fs, fileStatus, reference, null);
204  }
205
206  /**
207   * Create a Store File Info from an HFileLink and a Reference
208   * @param conf       The {@link Configuration} to use
209   * @param fs         The current file system to use
210   * @param fileStatus The {@link FileStatus} of the file
211   * @param reference  The reference instance
212   * @param link       The link instance
213   */
214  public StoreFileInfo(final Configuration conf, final FileSystem fs, final FileStatus fileStatus,
215    final Reference reference, final HFileLink link) {
216    this.fs = fs;
217    this.conf = conf;
218    this.primaryReplica = false;
219    this.initialPath = (fileStatus == null) ? null : fileStatus.getPath();
220    this.createdTimestamp = (fileStatus == null) ? 0 : fileStatus.getModificationTime();
221    this.reference = reference;
222    this.link = link;
223    this.noReadahead =
224      this.conf.getBoolean(STORE_FILE_READER_NO_READAHEAD, DEFAULT_STORE_FILE_READER_NO_READAHEAD);
225  }
226
227  @Override
228  public Configuration getConf() {
229    return conf;
230  }
231
232  @Override
233  public void setConf(Configuration conf) {
234    this.conf = conf;
235  }
236
237  /**
238   * Size of the Hfile
239   */
240  public long getSize() {
241    return size;
242  }
243
244  /**
245   * Sets the region coprocessor env.
246   */
247  public void setRegionCoprocessorHost(RegionCoprocessorHost coprocessorHost) {
248    this.coprocessorHost = coprocessorHost;
249  }
250
251  /**
252   * @return the Reference object associated to this StoreFileInfo. null if the StoreFile is not a
253   *         reference.
254   */
255  public Reference getReference() {
256    return this.reference;
257  }
258
259  /** Returns True if the store file is a Reference */
260  public boolean isReference() {
261    return this.reference != null;
262  }
263
264  /** Returns True if the store file is a top Reference */
265  public boolean isTopReference() {
266    return this.reference != null && Reference.isTopFileRegion(this.reference.getFileRegion());
267  }
268
269  /** Returns True if the store file is a link */
270  public boolean isLink() {
271    return this.link != null && this.reference == null;
272  }
273
274  /** Returns the HDFS block distribution */
275  public HDFSBlocksDistribution getHDFSBlockDistribution() {
276    return this.hdfsBlocksDistribution;
277  }
278
279  public StoreFileReader createReader(ReaderContext context, CacheConfig cacheConf)
280    throws IOException {
281    StoreFileReader reader = null;
282    if (this.reference != null) {
283      reader = new HalfStoreFileReader(context, hfileInfo, cacheConf, reference, this, conf);
284    } else {
285      reader = new StoreFileReader(context, hfileInfo, cacheConf, this, conf);
286    }
287    return reader;
288  }
289
290  ReaderContext createReaderContext(boolean doDropBehind, long readahead, ReaderType type)
291    throws IOException {
292    FSDataInputStreamWrapper in;
293    FileStatus status;
294    if (this.link != null) {
295      // HFileLink
296      in = new FSDataInputStreamWrapper(fs, this.link, doDropBehind, readahead);
297      status = this.link.getFileStatus(fs);
298    } else if (this.reference != null) {
299      // HFile Reference
300      Path referencePath = getReferredToFile(this.getPath());
301      try {
302        in = new FSDataInputStreamWrapper(fs, referencePath, doDropBehind, readahead);
303      } catch (FileNotFoundException fnfe) {
304        // Intercept the exception so can insert more info about the Reference; otherwise
305        // exception just complains about some random file -- operator doesn't realize it
306        // other end of a Reference
307        FileNotFoundException newFnfe = new FileNotFoundException(toString());
308        newFnfe.initCause(fnfe);
309        throw newFnfe;
310      }
311      status = fs.getFileStatus(referencePath);
312    } else {
313      in = new FSDataInputStreamWrapper(fs, this.getPath(), doDropBehind, readahead);
314      status = fs.getFileStatus(initialPath);
315    }
316    long length = status.getLen();
317    ReaderContextBuilder contextBuilder =
318      new ReaderContextBuilder().withInputStreamWrapper(in).withFileSize(length)
319        .withPrimaryReplicaReader(this.primaryReplica).withReaderType(type).withFileSystem(fs);
320    if (this.reference != null) {
321      contextBuilder.withFilePath(this.getPath());
322    } else {
323      contextBuilder.withFilePath(status.getPath());
324    }
325    return contextBuilder.build();
326  }
327
328  /**
329   * Compute the HDFS Block Distribution for this StoreFile
330   */
331  public HDFSBlocksDistribution computeHDFSBlocksDistribution(final FileSystem fs)
332    throws IOException {
333    // guard against the case where we get the FileStatus from link, but by the time we
334    // call compute the file is moved again
335    if (this.link != null) {
336      FileNotFoundException exToThrow = null;
337      for (int i = 0; i < this.link.getLocations().length; i++) {
338        try {
339          return computeHDFSBlocksDistributionInternal(fs);
340        } catch (FileNotFoundException ex) {
341          // try the other location
342          exToThrow = ex;
343        }
344      }
345      throw exToThrow;
346    } else {
347      return computeHDFSBlocksDistributionInternal(fs);
348    }
349  }
350
351  private HDFSBlocksDistribution computeHDFSBlocksDistributionInternal(final FileSystem fs)
352    throws IOException {
353    FileStatus status = getReferencedFileStatus(fs);
354    if (this.reference != null) {
355      return computeRefFileHDFSBlockDistribution(fs, reference, status);
356    } else {
357      return FSUtils.computeHDFSBlocksDistribution(fs, status, 0, status.getLen());
358    }
359  }
360
361  /**
362   * Get the {@link FileStatus} of the file referenced by this StoreFileInfo
363   * @param fs The current file system to use.
364   * @return The {@link FileStatus} of the file referenced by this StoreFileInfo
365   */
366  public FileStatus getReferencedFileStatus(final FileSystem fs) throws IOException {
367    FileStatus status;
368    if (this.reference != null) {
369      if (this.link != null) {
370        FileNotFoundException exToThrow = null;
371        for (int i = 0; i < this.link.getLocations().length; i++) {
372          // HFileLink Reference
373          try {
374            return link.getFileStatus(fs);
375          } catch (FileNotFoundException ex) {
376            // try the other location
377            exToThrow = ex;
378          }
379        }
380        throw exToThrow;
381      } else {
382        // HFile Reference
383        Path referencePath = getReferredToFile(this.getPath());
384        status = fs.getFileStatus(referencePath);
385      }
386    } else {
387      if (this.link != null) {
388        FileNotFoundException exToThrow = null;
389        for (int i = 0; i < this.link.getLocations().length; i++) {
390          // HFileLink
391          try {
392            return link.getFileStatus(fs);
393          } catch (FileNotFoundException ex) {
394            // try the other location
395            exToThrow = ex;
396          }
397        }
398        throw exToThrow;
399      } else {
400        status = fs.getFileStatus(initialPath);
401      }
402    }
403    return status;
404  }
405
406  /** Returns The {@link Path} of the file */
407  public Path getPath() {
408    return initialPath;
409  }
410
411  /** Returns The {@link FileStatus} of the file */
412  public FileStatus getFileStatus() throws IOException {
413    return getReferencedFileStatus(fs);
414  }
415
416  /** Returns Get the modification time of the file. */
417  public long getModificationTime() throws IOException {
418    return getFileStatus().getModificationTime();
419  }
420
421  @Override
422  public String toString() {
423    return this.getPath()
424      + (isReference() ? "->" + getReferredToFile(this.getPath()) + "-" + reference : "");
425  }
426
427  /**
428   * Cells in a bulkloaded file don't have a sequenceId since they don't go through memstore. When a
429   * bulkload file is committed, the current memstore ts is stamped onto the file name as the
430   * sequenceId of the file. At read time, the sequenceId is copied onto all of the cells returned
431   * so that they can be properly sorted relative to other cells in other files. Further, when
432   * opening multiple files for scan, the sequence id is used to ensusre that the bulkload file's
433   * scanner is porperly sorted amongst the other scanners. Non-bulkloaded files get their
434   * sequenceId from the MAX_MEMSTORE_TS_KEY since those go through the memstore and have true
435   * sequenceIds.
436   */
437  private static final String SEQ_ID_MARKER = "_SeqId_";
438  private static final int SEQ_ID_MARKER_LENGTH = SEQ_ID_MARKER.length();
439
440  /**
441   * @see #SEQ_ID_MARKER
442   * @return True if the file name looks like a bulkloaded file, based on the presence of the SeqId
443   *         marker added to those files.
444   */
445  public static boolean hasBulkloadSeqId(final Path path) {
446    String fileName = path.getName();
447    return fileName.contains(SEQ_ID_MARKER);
448  }
449
450  /**
451   * @see #SEQ_ID_MARKER
452   * @return If the path is a properly named bulkloaded file, returns the sequence id stamped at the
453   *         end of the file name.
454   */
455  public static OptionalLong getBulkloadSeqId(final Path path) {
456    String fileName = path.getName();
457    int startPos = fileName.indexOf(SEQ_ID_MARKER);
458    if (startPos != -1) {
459      String strVal = fileName.substring(startPos + SEQ_ID_MARKER_LENGTH,
460        fileName.indexOf('_', startPos + SEQ_ID_MARKER_LENGTH));
461      return OptionalLong.of(Long.parseLong(strVal));
462    }
463    return OptionalLong.empty();
464  }
465
466  /**
467   * @see #SEQ_ID_MARKER
468   * @return A string value for appending to the end of a bulkloaded file name, containing the
469   *         properly formatted SeqId marker.
470   */
471  public static String formatBulkloadSeqId(long seqId) {
472    return SEQ_ID_MARKER + seqId + "_";
473  }
474
475  /**
476   * @param path Path to check.
477   * @return True if the path has format of a HFile.
478   */
479  public static boolean isHFile(final Path path) {
480    return isHFile(path.getName());
481  }
482
483  public static boolean isHFile(final String fileName) {
484    Matcher m = HFILE_NAME_PATTERN.matcher(fileName);
485    return m.matches() && m.groupCount() > 0;
486  }
487
488  /**
489   * Checks if the file is a MOB file
490   * @param path path to a file
491   * @return true, if - yes, false otherwise
492   */
493  public static boolean isMobFile(final Path path) {
494    String fileName = path.getName();
495    String[] parts = fileName.split(MobUtils.SEP);
496    if (parts.length != 2) {
497      return false;
498    }
499    Matcher m = HFILE_NAME_PATTERN.matcher(parts[0]);
500    Matcher mm = HFILE_NAME_PATTERN.matcher(parts[1]);
501    return m.matches() && mm.matches();
502  }
503
504  /**
505   * Checks if the file is a MOB reference file, created by snapshot
506   * @param path path to a file
507   * @return true, if - yes, false otherwise
508   */
509  public static boolean isMobRefFile(final Path path) {
510    String fileName = path.getName();
511    int lastIndex = fileName.lastIndexOf(MobUtils.SEP);
512    if (lastIndex < 0) {
513      return false;
514    }
515    String[] parts = new String[2];
516    parts[0] = fileName.substring(0, lastIndex);
517    parts[1] = fileName.substring(lastIndex + 1);
518    String name = parts[0] + "." + parts[1];
519    Matcher m = REF_NAME_PATTERN.matcher(name);
520    return m.matches() && m.groupCount() > 1;
521  }
522
523  /**
524   * @param path Path to check.
525   * @return True if the path has format of a HStoreFile reference.
526   */
527  public static boolean isReference(final Path path) {
528    return isReference(path.getName());
529  }
530
531  /**
532   * @param name file name to check.
533   * @return True if the path has format of a HStoreFile reference.
534   */
535  public static boolean isReference(final String name) {
536    Matcher m = REF_NAME_PATTERN.matcher(name);
537    return m.matches() && m.groupCount() > 1;
538  }
539
540  /** Returns timestamp when this file was created (as returned by filesystem) */
541  public long getCreatedTimestamp() {
542    return createdTimestamp;
543  }
544
545  /*
546   * Return path to the file referred to by a Reference. Presumes a directory hierarchy of
547   * <code>${hbase.rootdir}/data/${namespace}/tablename/regionname/familyname</code>.
548   * @param p Path to a Reference file.
549   * @return Calculated path to parent region file.
550   * @throws IllegalArgumentException when path regex fails to match.
551   */
552  public static Path getReferredToFile(final Path p) {
553    Matcher m = REF_NAME_PATTERN.matcher(p.getName());
554    if (m == null || !m.matches()) {
555      LOG.warn("Failed match of store file name {}", p.toString());
556      throw new IllegalArgumentException("Failed match of store file name " + p.toString());
557    }
558
559    // Other region name is suffix on the passed Reference file name
560    String otherRegion = m.group(2);
561    // Tabledir is up two directories from where Reference was written.
562    Path tableDir = p.getParent().getParent().getParent();
563    String nameStrippedOfSuffix = m.group(1);
564    LOG.trace("reference {} to region={} hfile={}", p, otherRegion, nameStrippedOfSuffix);
565
566    // Build up new path with the referenced region in place of our current
567    // region in the reference path. Also strip regionname suffix from name.
568    return new Path(new Path(new Path(tableDir, otherRegion), p.getParent().getName()),
569      nameStrippedOfSuffix);
570  }
571
572  /*
573   * Return region and file name referred to by a Reference.
574   * @param referenceFile HFile name which is a Reference.
575   * @return Calculated referenced region and file name.
576   * @throws IllegalArgumentException when referenceFile regex fails to match.
577   */
578  public static Pair<String, String> getReferredToRegionAndFile(final String referenceFile) {
579    Matcher m = REF_NAME_PATTERN.matcher(referenceFile);
580    if (m == null || !m.matches()) {
581      LOG.warn("Failed match of store file name {}", referenceFile);
582      throw new IllegalArgumentException("Failed match of store file name " + referenceFile);
583    }
584    String referencedRegion = m.group(2);
585    String referencedFile = m.group(1);
586    LOG.trace("reference {} to region={} file={}", referenceFile, referencedRegion, referencedFile);
587    return new Pair<>(referencedRegion, referencedFile);
588  }
589
590  /**
591   * Validate the store file name.
592   * @param fileName name of the file to validate
593   * @return <tt>true</tt> if the file could be a valid store file, <tt>false</tt> otherwise
594   */
595  public static boolean validateStoreFileName(final String fileName) {
596    if (HFileLink.isHFileLink(fileName) || isReference(fileName)) {
597      return true;
598    }
599    return !fileName.contains("-");
600  }
601
602  /**
603   * Return if the specified file is a valid store file or not.
604   * @param fileStatus The {@link FileStatus} of the file
605   * @return <tt>true</tt> if the file is valid
606   */
607  public static boolean isValid(final FileStatus fileStatus) throws IOException {
608    final Path p = fileStatus.getPath();
609
610    if (fileStatus.isDirectory()) {
611      return false;
612    }
613
614    // Check for empty hfile. Should never be the case but can happen
615    // after data loss in hdfs for whatever reason (upgrade, etc.): HBASE-646
616    // NOTE: that the HFileLink is just a name, so it's an empty file.
617    if (!HFileLink.isHFileLink(p) && fileStatus.getLen() <= 0) {
618      LOG.warn("Skipping {} because it is empty. HBASE-646 DATA LOSS?", p);
619      return false;
620    }
621
622    return validateStoreFileName(p.getName());
623  }
624
625  /**
626   * helper function to compute HDFS blocks distribution of a given reference file.For reference
627   * file, we don't compute the exact value. We use some estimate instead given it might be good
628   * enough. we assume bottom part takes the first half of reference file, top part takes the second
629   * half of the reference file. This is just estimate, given midkey ofregion != midkey of HFile,
630   * also the number and size of keys vary. If this estimate isn't good enough, we can improve it
631   * later.
632   * @param fs        The FileSystem
633   * @param reference The reference
634   * @param status    The reference FileStatus
635   * @return HDFS blocks distribution
636   */
637  private static HDFSBlocksDistribution computeRefFileHDFSBlockDistribution(final FileSystem fs,
638    final Reference reference, final FileStatus status) throws IOException {
639    if (status == null) {
640      return null;
641    }
642
643    long start = 0;
644    long length = 0;
645
646    if (Reference.isTopFileRegion(reference.getFileRegion())) {
647      start = status.getLen() / 2;
648      length = status.getLen() - status.getLen() / 2;
649    } else {
650      start = 0;
651      length = status.getLen() / 2;
652    }
653    return FSUtils.computeHDFSBlocksDistribution(fs, status, start, length);
654  }
655
656  @Override
657  public boolean equals(Object that) {
658    if (this == that) {
659      return true;
660    }
661    if (that == null) {
662      return false;
663    }
664
665    if (!(that instanceof StoreFileInfo)) {
666      return false;
667    }
668
669    StoreFileInfo o = (StoreFileInfo) that;
670    if (initialPath != null && o.initialPath == null) {
671      return false;
672    }
673    if (initialPath == null && o.initialPath != null) {
674      return false;
675    }
676    if (initialPath != o.initialPath && initialPath != null && !initialPath.equals(o.initialPath)) {
677      return false;
678    }
679    if (reference != null && o.reference == null) {
680      return false;
681    }
682    if (reference == null && o.reference != null) {
683      return false;
684    }
685    if (reference != o.reference && reference != null && !reference.equals(o.reference)) {
686      return false;
687    }
688
689    if (link != null && o.link == null) {
690      return false;
691    }
692    if (link == null && o.link != null) {
693      return false;
694    }
695    if (link != o.link && link != null && !link.equals(o.link)) {
696      return false;
697    }
698
699    return true;
700  }
701
702  @Override
703  public int hashCode() {
704    int hash = 17;
705    hash = hash * 31 + ((reference == null) ? 0 : reference.hashCode());
706    hash = hash * 31 + ((initialPath == null) ? 0 : initialPath.hashCode());
707    hash = hash * 31 + ((link == null) ? 0 : link.hashCode());
708    return hash;
709  }
710
711  /**
712   * Return the active file name that contains the real data.
713   * <p>
714   * For referenced hfile, we will return the name of the reference file as it will be used to
715   * construct the StoreFileReader. And for linked hfile, we will return the name of the file being
716   * linked.
717   */
718  public String getActiveFileName() {
719    if (reference != null || link == null) {
720      return initialPath.getName();
721    } else {
722      return HFileLink.getReferencedHFileName(initialPath.getName());
723    }
724  }
725
726  FileSystem getFileSystem() {
727    return this.fs;
728  }
729
730  boolean isNoReadahead() {
731    return this.noReadahead;
732  }
733
734  public HFileInfo getHFileInfo() {
735    return hfileInfo;
736  }
737
738  void initHDFSBlocksDistribution() throws IOException {
739    hdfsBlocksDistribution = computeHDFSBlocksDistribution(fs);
740  }
741
742  StoreFileReader preStoreFileReaderOpen(ReaderContext context, CacheConfig cacheConf)
743    throws IOException {
744    StoreFileReader reader = null;
745    if (this.coprocessorHost != null) {
746      reader = this.coprocessorHost.preStoreFileReaderOpen(fs, this.getPath(),
747        context.getInputStreamWrapper(), context.getFileSize(), cacheConf, reference);
748    }
749    return reader;
750  }
751
752  StoreFileReader postStoreFileReaderOpen(ReaderContext context, CacheConfig cacheConf,
753    StoreFileReader reader) throws IOException {
754    StoreFileReader res = reader;
755    if (this.coprocessorHost != null) {
756      res = this.coprocessorHost.postStoreFileReaderOpen(fs, this.getPath(),
757        context.getInputStreamWrapper(), context.getFileSize(), cacheConf, reference, reader);
758    }
759    return res;
760  }
761
762  public void initHFileInfo(ReaderContext context) throws IOException {
763    this.hfileInfo = new HFileInfo(context, conf);
764  }
765
766  int getRefCount() {
767    return this.refCount.get();
768  }
769
770  int increaseRefCount() {
771    return this.refCount.incrementAndGet();
772  }
773
774  int decreaseRefCount() {
775    return this.refCount.decrementAndGet();
776  }
777
778}