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