View Javadoc

1   /**
2    *
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  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,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the License for the specific language governing permissions and
17   * limitations under the License.
18   */
19  
20  package org.apache.hadoop.hbase.client;
21  
22  import java.io.IOException;
23  import java.net.ConnectException;
24  import java.net.SocketTimeoutException;
25  
26  import org.apache.commons.logging.Log;
27  import org.apache.commons.logging.LogFactory;
28  import org.apache.hadoop.hbase.classification.InterfaceAudience;
29  import org.apache.hadoop.hbase.HRegionInfo;
30  import org.apache.hadoop.hbase.HRegionLocation;
31  import org.apache.hadoop.hbase.NotServingRegionException;
32  import org.apache.hadoop.hbase.TableName;
33  import org.apache.hadoop.hbase.exceptions.RegionMovedException;
34  import org.apache.hadoop.hbase.protobuf.generated.ClientProtos.ClientService;
35  import org.apache.hadoop.hbase.util.Bytes;
36  
37  /**
38   * Implementations call a RegionServer and implement {@link #call(int)}.
39   * Passed to a {@link RpcRetryingCaller} so we retry on fail.
40   * TODO: this class is actually tied to one region, because most of the paths make use of
41   *       the regioninfo part of location when building requests. The only reason it works for
42   *       multi-region requests (e.g. batch) is that they happen to not use the region parts.
43   *       This could be done cleaner (e.g. having a generic parameter and 2 derived classes,
44   *       RegionCallable and actual RegionServerCallable with ServerName.
45   * @param <T> the class that the ServerCallable handles
46   */
47  @InterfaceAudience.Private
48  public abstract class RegionServerCallable<T> implements RetryingCallable<T> {
49    // Public because used outside of this package over in ipc.
50    static final Log LOG = LogFactory.getLog(RegionServerCallable.class);
51    protected final Connection connection;
52    protected final TableName tableName;
53    protected final byte[] row;
54    protected HRegionLocation location;
55    private ClientService.BlockingInterface stub;
56  
57    protected final static int MIN_WAIT_DEAD_SERVER = 10000;
58  
59    /**
60     * @param connection Connection to use.
61     * @param tableName Table name to which <code>row</code> belongs.
62     * @param row The row we want in <code>tableName</code>.
63     */
64    public RegionServerCallable(Connection connection, TableName tableName, byte [] row) {
65      this.connection = connection;
66      this.tableName = tableName;
67      this.row = row;
68    }
69  
70    /**
71     * Prepare for connection to the server hosting region with row from tablename.  Does lookup
72     * to find region location and hosting server.
73     * @param reload Set this to true if connection should re-find the region
74     * @throws IOException e
75     */
76    @Override
77    public void prepare(final boolean reload) throws IOException {
78      try (RegionLocator regionLocator = connection.getRegionLocator(tableName)) {
79        this.location = regionLocator.getRegionLocation(row, reload);
80      }
81      if (this.location == null) {
82        throw new IOException("Failed to find location, tableName=" + tableName +
83          ", row=" + Bytes.toString(row) + ", reload=" + reload);
84      }
85      setStub(getConnection().getClient(this.location.getServerName()));
86    }
87  
88    /**
89     * @return {@link HConnection} instance used by this Callable.
90     */
91    HConnection getConnection() {
92      return (HConnection) this.connection;
93    }
94  
95    protected ClientService.BlockingInterface getStub() {
96      return this.stub;
97    }
98  
99    void setStub(final ClientService.BlockingInterface stub) {
100     this.stub = stub;
101   }
102 
103   protected HRegionLocation getLocation() {
104     return this.location;
105   }
106 
107   protected void setLocation(final HRegionLocation location) {
108     this.location = location;
109   }
110 
111   public TableName getTableName() {
112     return this.tableName;
113   }
114 
115   public byte [] getRow() {
116     return this.row;
117   }
118 
119   @Override
120   public void throwable(Throwable t, boolean retrying) {
121     if (t instanceof SocketTimeoutException ||
122         t instanceof ConnectException ||
123         t instanceof RetriesExhaustedException ||
124         (location != null && getConnection().isDeadServer(location.getServerName()))) {
125       // if thrown these exceptions, we clear all the cache entries that
126       // map to that slow/dead server; otherwise, let cache miss and ask
127       // hbase:meta again to find the new location
128       if (this.location != null) getConnection().clearCaches(location.getServerName());
129     } else if (t instanceof RegionMovedException) {
130       getConnection().updateCachedLocations(tableName, row, t, location);
131     } else if (t instanceof NotServingRegionException && !retrying) {
132       // Purge cache entries for this specific region from hbase:meta cache
133       // since we don't call connect(true) when number of retries is 1.
134       getConnection().deleteCachedRegionLocation(location);
135     }
136   }
137 
138   @Override
139   public String getExceptionMessageAdditionalDetail() {
140     return "row '" + Bytes.toString(row) + "' on table '" + tableName + "' at " + location;
141   }
142 
143   @Override
144   public long sleep(long pause, int tries) {
145     long sleep = ConnectionUtils.getPauseTime(pause, tries);
146     if (sleep < MIN_WAIT_DEAD_SERVER
147         && (location == null || getConnection().isDeadServer(location.getServerName()))) {
148       sleep = ConnectionUtils.addJitter(MIN_WAIT_DEAD_SERVER, 0.10f);
149     }
150     return sleep;
151   }
152 
153   /**
154    * @return the HRegionInfo for the current region
155    */
156   public HRegionInfo getHRegionInfo() {
157     if (this.location == null) {
158       return null;
159     }
160     return this.location.getRegionInfo();
161   }
162 }