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