/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.hadoop.hbase.regionserver; import static org.apache.phoenix.coprocessor.BaseScannerRegionObserver.SCAN_START_ROW_SUFFIX; import java.io.IOException; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.FileSystem; import org.apache.hadoop.fs.Path; import org.apache.hadoop.hbase.CellUtil; import org.apache.hadoop.hbase.KeyValue; import org.apache.hadoop.hbase.client.RegionInfo; import org.apache.hadoop.hbase.client.Scan; import org.apache.hadoop.hbase.io.FSDataInputStreamWrapper; import org.apache.hadoop.hbase.io.ImmutableBytesWritable; import org.apache.hadoop.hbase.io.Reference; import org.apache.hadoop.hbase.io.hfile.CacheConfig; import org.apache.hadoop.hbase.util.Bytes; import org.apache.phoenix.index.IndexMaintainer; /** * A facade for a {@link org.apache.hadoop.hbase.io.hfile.HFile.Reader} that serves up either the * top or bottom half of an HFile where 'bottom' is the first half of the file containing the keys * that sort lowest and 'top' is the second half of the file with keys that sort greater than those * of the bottom half. The top includes the split files midkey, of the key that follows if it does * not exist in the file. * * <p> * This type works in tandem with the {@link Reference} type. This class is used reading while * Reference is used writing. * * <p> * This file is not splitable. Calls to {@link #midkey()} return null. */ public class IndexHalfStoreFileReader extends StoreFileReader { private final boolean top; // This is the key we split around. Its the first possible entry on a row: // i.e. empty column and a timestamp of LATEST_TIMESTAMP. private final byte[] splitkey; private final byte[] splitRow; private final Map<ImmutableBytesWritable, IndexMaintainer> indexMaintainers; private final byte[][] viewConstants; private final int offset; private final RegionInfo childRegionInfo; private final byte[] regionStartKeyInHFile; private final AtomicInteger refCount; private final RegionInfo currentRegion; /** * @param fs * @param p * @param cacheConf * @param in * @param size * @param r * @param conf * @param indexMaintainers * @param viewConstants * @param regionInfo * @param regionStartKeyInHFile * @param splitKey * @throws IOException */ public IndexHalfStoreFileReader(final FileSystem fs, final Path p, final CacheConfig cacheConf, final FSDataInputStreamWrapper in, long size, final Reference r, final Configuration conf, final Map<ImmutableBytesWritable, IndexMaintainer> indexMaintainers, final byte[][] viewConstants, final RegionInfo regionInfo, byte[] regionStartKeyInHFile, byte[] splitKey, boolean primaryReplicaStoreFile, AtomicInteger refCount, RegionInfo currentRegion) throws IOException { super(fs, p, in, size, cacheConf, primaryReplicaStoreFile, refCount, false, conf); this.splitkey = splitKey == null ? r.getSplitKey() : splitKey; // Is it top or bottom half? this.top = Reference.isTopFileRegion(r.getFileRegion()); this.splitRow = CellUtil.cloneRow(new KeyValue.KeyOnlyKeyValue(splitkey)); this.indexMaintainers = indexMaintainers; this.viewConstants = viewConstants; this.childRegionInfo = regionInfo; this.regionStartKeyInHFile = regionStartKeyInHFile; this.offset = regionStartKeyInHFile.length; this.refCount = refCount; this.currentRegion = currentRegion; } public int getOffset() { return offset; } public byte[][] getViewConstants() { return viewConstants; } public Map<ImmutableBytesWritable, IndexMaintainer> getIndexMaintainers() { return indexMaintainers; } public RegionInfo getRegionInfo() { return childRegionInfo; } public byte[] getRegionStartKeyInHFile() { return regionStartKeyInHFile; } public byte[] getSplitkey() { return splitkey; } public byte[] getSplitRow() { return splitRow; } public boolean isTop() { return top; } @Override public StoreFileScanner getStoreFileScanner(boolean cacheBlocks, boolean pread, boolean isCompaction, long readPt, long scannerOrder, boolean canOptimizeForNonNullColumn) { refCount.incrementAndGet(); return new LocalIndexStoreFileScanner(this, cacheBlocks, pread, isCompaction, readPt, scannerOrder, canOptimizeForNonNullColumn); } @Override public boolean passesKeyRangeFilter(Scan scan) { if (scan.getAttribute(SCAN_START_ROW_SUFFIX) == null) { // Scan from compaction. return true; } byte[] startKey = currentRegion.getStartKey(); byte[] endKey = currentRegion.getEndKey(); // If the region start key is not the prefix of the scan start row then we can return empty // scanners. This is possible during merge where one of the child region scan should not return any // results as we go through merged region. int prefixLength = scan.getStartRow().length - scan.getAttribute(SCAN_START_ROW_SUFFIX).length; if (Bytes.compareTo(scan.getStartRow(), 0, prefixLength, (startKey.length == 0 ? new byte[endKey.length] : startKey), 0, (startKey.length == 0 ? endKey.length : startKey.length)) != 0) { return false; } return true; } }