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.classification.InterfaceAudience;
26  import org.apache.hadoop.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.util.Bytes;
33  
34  /**
35   * A reversed ScannerCallable which supports backward scanning.
36   */
37  @InterfaceAudience.Public
38  @InterfaceStability.Evolving
39  public class ReversedScannerCallable extends ScannerCallable {
40    /**
41     * The start row for locating regions. In reversed scanner, may locate the
42     * regions for a range of keys when doing
43     * {@link ReversedClientScanner#nextScanner(int, boolean)}
44     */
45    protected final byte[] locateStartRow;
46  
47    /**
48     * 
49     * @param connection
50     * @param tableName
51     * @param scan
52     * @param scanMetrics
53     * @param locateStartRow The start row for locating regions
54     */
55    public ReversedScannerCallable(HConnection connection, TableName tableName,
56        Scan scan, ScanMetrics scanMetrics, byte[] locateStartRow) {
57      super(connection, tableName, scan, scanMetrics);
58      this.locateStartRow = locateStartRow;
59    }
60  
61    /**
62     * @param reload force reload of server location
63     * @throws IOException
64     */
65    @Override
66    public void prepare(boolean reload) throws IOException {
67      if (!instantiated || reload) {
68        if (locateStartRow == null) {
69          // Just locate the region with the row
70          this.location = connection.getRegionLocation(tableName, row, reload);
71          if (this.location == null) {
72            throw new IOException("Failed to find location, tableName="
73                + tableName + ", row=" + Bytes.toString(row) + ", reload="
74                + reload);
75          }
76        } else {
77          // Need to locate the regions with the range, and the target location is
78          // the last one which is the previous region of last region scanner
79          List<HRegionLocation> locatedRegions = locateRegionsInRange(
80              locateStartRow, row, reload);
81          if (locatedRegions.isEmpty()) {
82            throw new DoNotRetryIOException(
83                "Does hbase:meta exist hole? Couldn't get regions for the range from "
84                    + Bytes.toStringBinary(locateStartRow) + " to "
85                    + Bytes.toStringBinary(row));
86          }
87          this.location = locatedRegions.get(locatedRegions.size() - 1);
88        }
89        setStub(getConnection().getClient(getLocation().getServerName()));
90        checkIfRegionServerIsRemote();
91        instantiated = true;
92      }
93  
94      // check how often we retry.
95      // HConnectionManager will call instantiateServer with reload==true
96      // if and only if for retries.
97      if (reload && this.scanMetrics != null) {
98        this.scanMetrics.countOfRPCRetries.incrementAndGet();
99        if (isRegionServerRemote) {
100         this.scanMetrics.countOfRemoteRPCRetries.incrementAndGet();
101       }
102     }
103   }
104 
105   /**
106    * Get the corresponding regions for an arbitrary range of keys.
107    * @param startKey Starting row in range, inclusive
108    * @param endKey Ending row in range, exclusive
109    * @param reload force reload of server location
110    * @return A list of HRegionLocation corresponding to the regions that contain
111    *         the specified range
112    * @throws IOException
113    */
114   private List<HRegionLocation> locateRegionsInRange(byte[] startKey,
115       byte[] endKey, boolean reload) throws IOException {
116     final boolean endKeyIsEndOfTable = Bytes.equals(endKey,
117         HConstants.EMPTY_END_ROW);
118     if ((Bytes.compareTo(startKey, endKey) > 0) && !endKeyIsEndOfTable) {
119       throw new IllegalArgumentException("Invalid range: "
120           + Bytes.toStringBinary(startKey) + " > "
121           + Bytes.toStringBinary(endKey));
122     }
123     List<HRegionLocation> regionList = new ArrayList<HRegionLocation>();
124     byte[] currentKey = startKey;
125     do {
126       HRegionLocation regionLocation = connection.getRegionLocation(tableName,
127           currentKey, reload);
128       if (regionLocation.getRegionInfo().containsRow(currentKey)) {
129         regionList.add(regionLocation);
130       } else {
131         throw new DoNotRetryIOException("Does hbase:meta exist hole? Locating row "
132             + Bytes.toStringBinary(currentKey) + " returns incorrect region "
133             + regionLocation.getRegionInfo());
134       }
135       currentKey = regionLocation.getRegionInfo().getEndKey();
136     } while (!Bytes.equals(currentKey, HConstants.EMPTY_END_ROW)
137         && (endKeyIsEndOfTable || Bytes.compareTo(currentKey, endKey) < 0));
138     return regionList;
139   }
140 
141 }