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.io.hfile;
019
020import java.io.IOException;
021import org.apache.hadoop.conf.Configuration;
022import org.apache.hadoop.fs.Path;
023import org.apache.yetus.audience.InterfaceAudience;
024import org.slf4j.Logger;
025import org.slf4j.LoggerFactory;
026
027/**
028 * Implementation of {@link HFile.Reader} to deal with pread.
029 */
030@InterfaceAudience.Private
031public class HFilePreadReader extends HFileReaderImpl {
032  private static final Logger LOG = LoggerFactory.getLogger(HFileReaderImpl.class);
033
034  public HFilePreadReader(ReaderContext context, HFileInfo fileInfo,
035      CacheConfig cacheConf, Configuration conf) throws IOException {
036    super(context, fileInfo, cacheConf, conf);
037    // Prefetch file blocks upon open if requested
038    if (cacheConf.shouldPrefetchOnOpen()) {
039      PrefetchExecutor.request(path, new Runnable() {
040        @Override
041        public void run() {
042          long offset = 0;
043          long end = 0;
044          try {
045            end = getTrailer().getLoadOnOpenDataOffset();
046            if (LOG.isTraceEnabled()) {
047              LOG.trace("Prefetch start " + getPathOffsetEndStr(path, offset, end));
048            }
049            // Don't use BlockIterator here, because it's designed to read load-on-open section.
050            long onDiskSizeOfNextBlock = -1;
051            while (offset < end) {
052              if (Thread.interrupted()) {
053                break;
054              }
055              // Perhaps we got our block from cache? Unlikely as this may be, if it happens, then
056              // the internal-to-hfileblock thread local which holds the overread that gets the
057              // next header, will not have happened...so, pass in the onDiskSize gotten from the
058              // cached block. This 'optimization' triggers extremely rarely I'd say.
059              HFileBlock block = readBlock(offset, onDiskSizeOfNextBlock, /* cacheBlock= */true,
060                /* pread= */true, false, false, null, null);
061              try {
062                onDiskSizeOfNextBlock = block.getNextBlockOnDiskSize();
063                offset += block.getOnDiskSizeWithHeader();
064              } finally {
065                // Ideally here the readBlock won't find the block in cache. We call this
066                // readBlock so that block data is read from FS and cached in BC. we must call
067                // returnBlock here to decrease the reference count of block.
068                block.release();
069              }
070            }
071          } catch (IOException e) {
072            // IOExceptions are probably due to region closes (relocation, etc.)
073            if (LOG.isTraceEnabled()) {
074              LOG.trace("Prefetch " + getPathOffsetEndStr(path, offset, end), e);
075            }
076          } catch (NullPointerException e) {
077            LOG.warn("Stream moved/closed or prefetch cancelled?" +
078                getPathOffsetEndStr(path, offset, end), e);
079          } catch (Exception e) {
080            // Other exceptions are interesting
081            LOG.warn("Prefetch " + getPathOffsetEndStr(path, offset, end), e);
082          } finally {
083            PrefetchExecutor.complete(path);
084          }
085        }
086      });
087    }
088  }
089
090  private static String getPathOffsetEndStr(final Path path, final long offset, final long end) {
091    return "path=" + path.toString() + ", offset=" + offset + ", end=" + end;
092  }
093
094  public void close(boolean evictOnClose) throws IOException {
095    PrefetchExecutor.cancel(path);
096    // Deallocate blocks in load-on-open section
097    this.fileInfo.close();
098    // Deallocate data blocks
099    cacheConf.getBlockCache().ifPresent(cache -> {
100      if (evictOnClose) {
101        int numEvicted = cache.evictBlocksByHfileName(name);
102        if (LOG.isTraceEnabled()) {
103          LOG.trace("On close, file=" + name + " evicted=" + numEvicted + " block(s)");
104        }
105      }
106    });
107    fsBlockReader.closeStreams();
108  }
109}