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 */
018
019package org.apache.hadoop.hbase.client;
020
021import java.io.IOException;
022import java.util.ArrayList;
023import java.util.List;
024import org.apache.hadoop.conf.Configuration;
025import org.apache.hadoop.fs.FileSystem;
026import org.apache.hadoop.fs.Path;
027import org.apache.hadoop.hbase.Cell;
028import org.apache.hadoop.hbase.PrivateCellUtil;
029import org.apache.hadoop.hbase.client.metrics.ScanMetrics;
030import org.apache.hadoop.hbase.mob.MobFileCache;
031import org.apache.hadoop.hbase.regionserver.HRegion;
032import org.apache.hadoop.hbase.regionserver.RegionScanner;
033import org.apache.hadoop.hbase.util.CommonFSUtils;
034import org.apache.yetus.audience.InterfaceAudience;
035import org.slf4j.Logger;
036import org.slf4j.LoggerFactory;
037
038/**
039 * A client scanner for a region opened for read-only on the client side. Assumes region data
040 * is not changing.
041 */
042@InterfaceAudience.Private
043public class ClientSideRegionScanner extends AbstractClientScanner {
044
045  private static final Logger LOG = LoggerFactory.getLogger(ClientSideRegionScanner.class);
046
047  private HRegion region;
048  RegionScanner scanner;
049  List<Cell> values;
050
051  public ClientSideRegionScanner(Configuration conf, FileSystem fs,
052      Path rootDir, TableDescriptor htd, RegionInfo hri, Scan scan, ScanMetrics scanMetrics)
053      throws IOException {
054    // region is immutable, set isolation level
055    scan.setIsolationLevel(IsolationLevel.READ_UNCOMMITTED);
056
057    htd = TableDescriptorBuilder.newBuilder(htd).setReadOnly(true).build();
058
059    // open region from the snapshot directory
060    region = HRegion.newHRegion(CommonFSUtils.getTableDir(rootDir, htd.getTableName()), null, fs,
061      conf, hri, htd, null);
062    region.setRestoredRegion(true);
063    // we won't initialize the MobFileCache when not running in RS process. so provided an
064    // initialized cache. Consider the case: an CF was set from an mob to non-mob. if we only
065    // initialize cache for MOB region, NPE from HMobStore will still happen. So Initialize the
066    // cache for every region although it may hasn't any mob CF, BTW the cache is very light-weight.
067    region.setMobFileCache(new MobFileCache(conf));
068    region.initialize();
069
070    // create an internal region scanner
071    this.scanner = region.getScanner(scan);
072    values = new ArrayList<>();
073
074    if (scanMetrics == null) {
075      initScanMetrics(scan);
076    } else {
077      this.scanMetrics = scanMetrics;
078    }
079    region.startRegionOperation();
080  }
081
082  @Override
083  public Result next() throws IOException {
084    values.clear();
085    scanner.nextRaw(values);
086    if (values.isEmpty()) {
087      //we are done
088      return null;
089    }
090
091    Result result = Result.create(values);
092    if (this.scanMetrics != null) {
093      long resultSize = 0;
094      for (Cell cell : values) {
095        resultSize += PrivateCellUtil.estimatedSerializedSizeOf(cell);
096      }
097      this.scanMetrics.countOfBytesInResults.addAndGet(resultSize);
098      this.scanMetrics.countOfRowsScanned.incrementAndGet();
099    }
100
101    return result;
102  }
103
104  @Override
105  public void close() {
106    if (this.scanner != null) {
107      try {
108        this.scanner.close();
109        this.scanner = null;
110      } catch (IOException ex) {
111        LOG.warn("Exception while closing scanner", ex);
112      }
113    }
114    if (this.region != null) {
115      try {
116        this.region.closeRegionOperation();
117        this.region.close(true);
118        this.region = null;
119      } catch (IOException ex) {
120        LOG.warn("Exception while closing region", ex);
121      }
122    }
123  }
124
125  @Override
126  public boolean renewLease() {
127    throw new UnsupportedOperationException();
128  }
129}