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.IOException; 021import java.util.ArrayList; 022import java.util.Collections; 023import java.util.HashSet; 024import java.util.List; 025import java.util.NavigableSet; 026import java.util.Set; 027import org.apache.hadoop.fs.Path; 028import org.apache.hadoop.hbase.ExtendedCell; 029import org.apache.hadoop.hbase.client.Scan; 030import org.apache.hadoop.hbase.mob.MobCell; 031import org.apache.hadoop.hbase.mob.MobUtils; 032import org.apache.yetus.audience.InterfaceAudience; 033import org.slf4j.Logger; 034import org.slf4j.LoggerFactory; 035 036/** 037 * Scanner scans both the memstore and the MOB Store. Coalesce KeyValue stream into 038 * {@code List<KeyValue>} for a single row. 039 */ 040@InterfaceAudience.Private 041public class MobStoreScanner extends StoreScanner { 042 043 private static final Logger LOG = LoggerFactory.getLogger(MobStoreScanner.class); 044 045 private boolean cacheMobBlocks = false; 046 private boolean rawMobScan = false; 047 private boolean readEmptyValueOnMobCellMiss = false; 048 private final HMobStore mobStore; 049 private final List<MobCell> referencedMobCells; 050 private final Set<Path> mobFilesRead = new HashSet<>(); 051 052 public MobStoreScanner(HStore store, ScanInfo scanInfo, Scan scan, 053 final NavigableSet<byte[]> columns, long readPt) throws IOException { 054 super(store, scanInfo, scan, columns, readPt); 055 cacheMobBlocks = MobUtils.isCacheMobBlocks(scan); 056 rawMobScan = MobUtils.isRawMobScan(scan); 057 readEmptyValueOnMobCellMiss = MobUtils.isReadEmptyValueOnMobCellMiss(scan); 058 if (!(store instanceof HMobStore)) { 059 throw new IllegalArgumentException("The store " + store + " is not a HMobStore"); 060 } 061 mobStore = (HMobStore) store; 062 this.referencedMobCells = new ArrayList<>(); 063 } 064 065 /** 066 * Firstly reads the cells from the HBase. If the cell are a reference cell (which has the 067 * reference tag), the scanner need seek this cell from the mob file, and use the cell found from 068 * the mob file as the result. 069 */ 070 @Override 071 public boolean next(List<? super ExtendedCell> outResult, ScannerContext ctx) throws IOException { 072 boolean result = super.next(outResult, ctx); 073 if (!rawMobScan) { 074 // retrieve the mob data 075 if (outResult.isEmpty()) { 076 return result; 077 } 078 long mobKVCount = 0; 079 long mobKVSize = 0; 080 for (int i = 0; i < outResult.size(); i++) { 081 // At server side, we should only get ExtendedCell 082 ExtendedCell cell = (ExtendedCell) outResult.get(i); 083 if (MobUtils.isMobReferenceCell(cell)) { 084 MobCell mobCell = 085 mobStore.resolve(cell, cacheMobBlocks, readPt, readEmptyValueOnMobCellMiss); 086 mobKVCount++; 087 mobKVSize += mobCell.getCell().getValueLength(); 088 outResult.set(i, mobCell.getCell()); 089 // Keep the MobCell here unless we shipped the RPC or close the scanner. 090 referencedMobCells.add(mobCell); 091 } 092 } 093 mobStore.updateMobScanCellsCount(mobKVCount); 094 mobStore.updateMobScanCellsSize(mobKVSize); 095 } 096 return result; 097 } 098 099 private void freeAllReferencedMobCells() throws IOException { 100 for (MobCell cell : referencedMobCells) { 101 cell.close(); 102 mobFilesRead.addAll(cell.getFilesRead()); 103 } 104 referencedMobCells.clear(); 105 } 106 107 /** 108 * Returns the set of store file paths and MOB file paths successfully read by this scanner. 109 * Combines paths from the underlying store scanner with paths from resolved MOB cells (populated 110 * when referenced mob cells are closed, e.g. in close() or shipped()). 111 */ 112 @Override 113 public Set<Path> getFilesRead() { 114 Set<Path> allFiles = new HashSet<>(super.getFilesRead()); 115 allFiles.addAll(mobFilesRead); 116 return Collections.unmodifiableSet(allFiles); 117 } 118 119 @Override 120 public void shipped() throws IOException { 121 super.shipped(); 122 this.freeAllReferencedMobCells(); 123 } 124 125 @Override 126 public void close() { 127 super.close(); 128 try { 129 this.freeAllReferencedMobCells(); 130 } catch (IOException e) { 131 LOG.warn("Failed to free referenced mob cells: ", e); 132 } 133 } 134}