View Javadoc

1   /**
2    * Copyright The Apache Software Foundation
3    *
4    * Licensed to the Apache Software Foundation (ASF) under one or more
5    * contributor license agreements. See the NOTICE file distributed with this
6    * work for additional information regarding copyright ownership. The ASF
7    * licenses this file to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance with the License.
9    * You may obtain a copy of the License at
10   *
11   * http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
15   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
16   * License for the specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.hadoop.hbase.client;
20  
21  import java.io.IOException;
22  import java.util.ArrayList;
23  import java.util.List;
24  
25  import org.apache.hadoop.hbase.classification.InterfaceAudience;
26  import org.apache.hadoop.hbase.classification.InterfaceStability;
27  import org.apache.hadoop.hbase.DoNotRetryIOException;
28  import org.apache.hadoop.hbase.HConstants;
29  import org.apache.hadoop.hbase.HRegionLocation;
30  import org.apache.hadoop.hbase.TableName;
31  import org.apache.hadoop.hbase.client.metrics.ScanMetrics;
32  import org.apache.hadoop.hbase.ipc.PayloadCarryingRpcController;
33  import org.apache.hadoop.hbase.ipc.RpcControllerFactory;
34  import org.apache.hadoop.hbase.util.Bytes;
35  
36  /**
37   * A reversed ScannerCallable which supports backward scanning.
38   */
39  @InterfaceAudience.Public
40  @InterfaceStability.Evolving
41  public class ReversedScannerCallable extends ScannerCallable {
42    /**
43     * The start row for locating regions. In reversed scanner, may locate the
44     * regions for a range of keys when doing
45     * {@link ReversedClientScanner#nextScanner(int, boolean)}
46     */
47    protected final byte[] locateStartRow;
48  
49    @Deprecated
50    public ReversedScannerCallable(HConnection connection, TableName tableName, Scan scan,
51        ScanMetrics scanMetrics, byte[] locateStartRow) {
52      this(connection, tableName, scan, scanMetrics, locateStartRow, RpcControllerFactory
53          .instantiate(connection.getConfiguration()).newController());
54    }
55  
56    /**
57     * @param connection
58     * @param tableName
59     * @param scan
60     * @param scanMetrics
61     * @param locateStartRow The start row for locating regions
62     * @param rpcFactory
63     */
64    public ReversedScannerCallable(HConnection connection, TableName tableName, Scan scan,
65        ScanMetrics scanMetrics, byte[] locateStartRow, PayloadCarryingRpcController rpcFactory) {
66      super(connection, tableName, scan, scanMetrics, rpcFactory);
67      this.locateStartRow = locateStartRow;
68    }
69  
70    /**
71     * @param reload force reload of server location
72     * @throws IOException
73     */
74    @Override
75    public void prepare(boolean reload) throws IOException {
76      if (!instantiated || reload) {
77        if (locateStartRow == null) {
78          // Just locate the region with the row
79          this.location = connection.getRegionLocation(tableName, row, reload);
80          if (this.location == null) {
81            throw new IOException("Failed to find location, tableName="
82                + tableName + ", row=" + Bytes.toStringBinary(row) + ", reload="
83                + reload);
84          }
85        } else {
86          // Need to locate the regions with the range, and the target location is
87          // the last one which is the previous region of last region scanner
88          List<HRegionLocation> locatedRegions = locateRegionsInRange(
89              locateStartRow, row, reload);
90          if (locatedRegions.isEmpty()) {
91            throw new DoNotRetryIOException(
92                "Does hbase:meta exist hole? Couldn't get regions for the range from "
93                    + Bytes.toStringBinary(locateStartRow) + " to "
94                    + Bytes.toStringBinary(row));
95          }
96          this.location = locatedRegions.get(locatedRegions.size() - 1);
97        }
98        setStub(getConnection().getClient(getLocation().getServerName()));
99        checkIfRegionServerIsRemote();
100       instantiated = true;
101     }
102 
103     // check how often we retry.
104     // HConnectionManager will call instantiateServer with reload==true
105     // if and only if for retries.
106     if (reload && this.scanMetrics != null) {
107       this.scanMetrics.countOfRPCRetries.incrementAndGet();
108       if (isRegionServerRemote) {
109         this.scanMetrics.countOfRemoteRPCRetries.incrementAndGet();
110       }
111     }
112   }
113 
114   /**
115    * Get the corresponding regions for an arbitrary range of keys.
116    * @param startKey Starting row in range, inclusive
117    * @param endKey Ending row in range, exclusive
118    * @param reload force reload of server location
119    * @return A list of HRegionLocation corresponding to the regions that contain
120    *         the specified range
121    * @throws IOException
122    */
123   private List<HRegionLocation> locateRegionsInRange(byte[] startKey,
124       byte[] endKey, boolean reload) throws IOException {
125     final boolean endKeyIsEndOfTable = Bytes.equals(endKey,
126         HConstants.EMPTY_END_ROW);
127     if ((Bytes.compareTo(startKey, endKey) > 0) && !endKeyIsEndOfTable) {
128       throw new IllegalArgumentException("Invalid range: "
129           + Bytes.toStringBinary(startKey) + " > "
130           + Bytes.toStringBinary(endKey));
131     }
132     List<HRegionLocation> regionList = new ArrayList<HRegionLocation>();
133     byte[] currentKey = startKey;
134     do {
135       HRegionLocation regionLocation = connection.getRegionLocation(tableName,
136           currentKey, reload);
137       if (regionLocation.getRegionInfo().containsRow(currentKey)) {
138         regionList.add(regionLocation);
139       } else {
140         throw new DoNotRetryIOException("Does hbase:meta exist hole? Locating row "
141             + Bytes.toStringBinary(currentKey) + " returns incorrect region "
142             + regionLocation.getRegionInfo());
143       }
144       currentKey = regionLocation.getRegionInfo().getEndKey();
145     } while (!Bytes.equals(currentKey, HConstants.EMPTY_END_ROW)
146         && (endKeyIsEndOfTable || Bytes.compareTo(currentKey, endKey) < 0));
147     return regionList;
148   }
149 
150 }