001/**
002 *
003 * Licensed to the Apache Software Foundation (ASF) under one
004 * or more contributor license agreements.  See the NOTICE file
005 * distributed with this work for additional information
006 * regarding copyright ownership.  The ASF licenses this file
007 * to you under the Apache License, Version 2.0 (the
008 * "License"); you may not use this file except in compliance
009 * with the License.  You may obtain a copy of the License at
010 *
011 *     http://www.apache.org/licenses/LICENSE-2.0
012 *
013 * Unless required by applicable law or agreed to in writing, software
014 * distributed under the License is distributed on an "AS IS" BASIS,
015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
016 * See the License for the specific language governing permissions and
017 * limitations under the License.
018 */
019package org.apache.hadoop.hbase.regionserver;
020
021import java.io.IOException;
022import java.util.ArrayList;
023import java.util.List;
024import java.util.NavigableSet;
025
026import org.apache.hadoop.hbase.Cell;
027import org.apache.hadoop.hbase.client.Scan;
028import org.apache.hadoop.hbase.mob.MobCell;
029import org.apache.hadoop.hbase.mob.MobUtils;
030import org.apache.yetus.audience.InterfaceAudience;
031import org.slf4j.Logger;
032import org.slf4j.LoggerFactory;
033
034/**
035 * Scanner scans both the memstore and the MOB Store. Coalesce KeyValue stream into
036 * {@code List<KeyValue>} for a single row.
037 */
038@InterfaceAudience.Private
039public class MobStoreScanner extends StoreScanner {
040
041  private static final Logger LOG = LoggerFactory.getLogger(MobStoreScanner.class);
042
043  private boolean cacheMobBlocks = false;
044  private boolean rawMobScan = false;
045  private boolean readEmptyValueOnMobCellMiss = false;
046  private final HMobStore mobStore;
047  private final List<MobCell> referencedMobCells;
048
049  public MobStoreScanner(HStore store, ScanInfo scanInfo, Scan scan,
050      final NavigableSet<byte[]> columns, long readPt) throws IOException {
051    super(store, scanInfo, scan, columns, readPt);
052    cacheMobBlocks = MobUtils.isCacheMobBlocks(scan);
053    rawMobScan = MobUtils.isRawMobScan(scan);
054    readEmptyValueOnMobCellMiss = MobUtils.isReadEmptyValueOnMobCellMiss(scan);
055    if (!(store instanceof HMobStore)) {
056      throw new IllegalArgumentException("The store " + store + " is not a HMobStore");
057    }
058    mobStore = (HMobStore) store;
059    this.referencedMobCells = new ArrayList<>();
060  }
061
062  /**
063   * Firstly reads the cells from the HBase. If the cell are a reference cell (which has the
064   * reference tag), the scanner need seek this cell from the mob file, and use the cell found
065   * from the mob file as the result.
066   */
067  @Override
068  public boolean next(List<Cell> outResult, ScannerContext ctx) throws IOException {
069    boolean result = super.next(outResult, ctx);
070    if (!rawMobScan) {
071      // retrieve the mob data
072      if (outResult.isEmpty()) {
073        return result;
074      }
075      long mobKVCount = 0;
076      long mobKVSize = 0;
077      for (int i = 0; i < outResult.size(); i++) {
078        Cell cell = outResult.get(i);
079        if (MobUtils.isMobReferenceCell(cell)) {
080          MobCell mobCell =
081              mobStore.resolve(cell, cacheMobBlocks, readPt, readEmptyValueOnMobCellMiss);
082          mobKVCount++;
083          mobKVSize += mobCell.getCell().getValueLength();
084          outResult.set(i, mobCell.getCell());
085          // Keep the MobCell here unless we shipped the RPC or close the scanner.
086          referencedMobCells.add(mobCell);
087        }
088      }
089      mobStore.updateMobScanCellsCount(mobKVCount);
090      mobStore.updateMobScanCellsSize(mobKVSize);
091    }
092    return result;
093  }
094
095  private void freeAllReferencedMobCells() throws IOException {
096    for (MobCell cell : referencedMobCells) {
097      cell.close();
098    }
099    referencedMobCells.clear();
100  }
101
102  @Override
103  public void shipped() throws IOException {
104    super.shipped();
105    this.freeAllReferencedMobCells();
106  }
107
108  @Override
109  public void close() {
110    super.close();
111    try {
112      this.freeAllReferencedMobCells();
113    } catch (IOException e) {
114      LOG.warn("Failed to free referenced mob cells: ", e);
115    }
116  }
117}