View Javadoc

1   /**
2    * Copyright 2010 The Apache Software Foundation
3    *
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *     http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing, software
15   * distributed under the License is distributed on an "AS IS" BASIS,
16   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17   * See the License for the specific language governing permissions and
18   * limitations under the License.
19   */
20  
21  package org.apache.hadoop.hbase.client;
22  
23  import java.io.IOException;
24  import java.lang.reflect.UndeclaredThrowableException;
25  import java.net.ConnectException;
26  import java.net.SocketTimeoutException;
27  import java.util.ArrayList;
28  import java.util.List;
29  import java.util.concurrent.Callable;
30  
31  import org.apache.hadoop.conf.Configuration;
32  import org.apache.hadoop.hbase.DoNotRetryIOException;
33  import org.apache.hadoop.hbase.HConstants;
34  import org.apache.hadoop.hbase.HRegionLocation;
35  import org.apache.hadoop.hbase.ipc.HBaseRPC;
36  import org.apache.hadoop.hbase.ipc.HRegionInterface;
37  import org.apache.hadoop.hbase.util.Bytes;
38  import org.apache.hadoop.ipc.RemoteException;
39  
40  /**
41   * Abstract class that implements {@link Callable}.  Implementation stipulates
42   * return type and method we actually invoke on remote Server.  Usually
43   * used inside a try/catch that fields usual connection failures all wrapped
44   * up in a retry loop.
45   * <p>Call {@link #connect(boolean)} to connect to server hosting region
46   * that contains the passed row in the passed table before invoking
47   * {@link #call()}.
48   * @see HConnection#getRegionServerWithoutRetries(ServerCallable)
49   * @param <T> the class that the ServerCallable handles
50   */
51  public abstract class ServerCallable<T> implements Callable<T> {
52    protected final HConnection connection;
53    protected final byte [] tableName;
54    protected final byte [] row;
55    protected HRegionLocation location;
56    protected HRegionInterface server;
57    protected int callTimeout;
58    protected long startTime, endTime;
59  
60    /**
61     * @param connection Connection to use.
62     * @param tableName Table name to which <code>row</code> belongs.
63     * @param row The row we want in <code>tableName</code>.
64     */
65    public ServerCallable(HConnection connection, byte [] tableName, byte [] row) {
66      this(connection, tableName, row, HConstants.DEFAULT_HBASE_CLIENT_OPERATION_TIMEOUT);
67    }
68  
69    public ServerCallable(HConnection connection, byte [] tableName, byte [] row, int callTimeout) {
70      this.connection = connection;
71      this.tableName = tableName;
72      this.row = row;
73      this.callTimeout = callTimeout;
74    }
75  
76    /**
77     * Connect to the server hosting region with row from tablename.
78     * @param reload Set this to true if connection should re-find the region
79     * @throws IOException e
80     */
81    public void connect(final boolean reload) throws IOException {
82      this.location = connection.getRegionLocation(tableName, row, reload);
83      this.server = connection.getHRegionConnection(location.getHostname(),
84        location.getPort());
85    }
86  
87    /** @return the server name
88     * @deprecated Just use {@link #toString()} instead.
89     */
90    public String getServerName() {
91      if (location == null) return null;
92      return location.getHostnamePort();
93    }
94  
95    /** @return the region name
96     * @deprecated Just use {@link #toString()} instead.
97     */
98    public byte[] getRegionName() {
99      if (location == null) return null;
100     return location.getRegionInfo().getRegionName();
101   }
102 
103   /** @return the row
104    * @deprecated Just use {@link #toString()} instead.
105    */
106   public byte [] getRow() {
107     return row;
108   }
109 
110   public void beforeCall() {
111     HBaseRPC.setRpcTimeout(this.callTimeout);
112     this.startTime = System.currentTimeMillis();
113   }
114 
115   public void afterCall() {
116     HBaseRPC.resetRpcTimeout();
117     this.endTime = System.currentTimeMillis();
118   }
119 
120   public void shouldRetry(Throwable throwable) throws IOException {
121     if (this.callTimeout != HConstants.DEFAULT_HBASE_CLIENT_OPERATION_TIMEOUT)
122       if (throwable instanceof SocketTimeoutException
123           || (this.endTime - this.startTime > this.callTimeout)) {
124         throw (SocketTimeoutException) (SocketTimeoutException) new SocketTimeoutException(
125             "Call to access row '" + Bytes.toString(row) + "' on table '"
126                 + Bytes.toString(tableName)
127                 + "' failed on socket timeout exception: " + throwable)
128             .initCause(throwable);
129       } else {
130         this.callTimeout = ((int) (this.endTime - this.startTime));
131       }
132   }
133 
134   /**
135    * @return {@link HConnection} instance used by this Callable.
136    */
137   HConnection getConnection() {
138     return this.connection;
139   }
140 
141   /**
142    * Run this instance with retries, timed waits,
143    * and refinds of missing regions.
144    *
145    * @param <T> the type of the return value
146    * @return an object of type T
147    * @throws IOException if a remote or network exception occurs
148    * @throws RuntimeException other unspecified error
149    */
150   public T withRetries()
151   throws IOException, RuntimeException {
152     Configuration c = getConnection().getConfiguration();
153     final long pause = c.getLong(HConstants.HBASE_CLIENT_PAUSE,
154       HConstants.DEFAULT_HBASE_CLIENT_PAUSE);
155     final int numRetries = c.getInt(HConstants.HBASE_CLIENT_RETRIES_NUMBER,
156       HConstants.DEFAULT_HBASE_CLIENT_RETRIES_NUMBER);
157     List<RetriesExhaustedException.ThrowableWithExtraContext> exceptions =
158       new ArrayList<RetriesExhaustedException.ThrowableWithExtraContext>();
159     for (int tries = 0; tries < numRetries; tries++) {
160       try {
161         beforeCall();
162         connect(tries != 0);
163         return call();
164       } catch (Throwable t) {
165         shouldRetry(t);
166         t = translateException(t);
167         if (t instanceof SocketTimeoutException ||
168             t instanceof ConnectException ||
169             t instanceof RetriesExhaustedException) {
170           // if thrown these exceptions, we clear all the cache entries that
171           // map to that slow/dead server; otherwise, let cache miss and ask
172           // .META. again to find the new location
173           HRegionLocation hrl = location;
174           if (hrl != null) {
175             getConnection().clearCaches(hrl.getHostnamePort());
176           }
177         }
178         RetriesExhaustedException.ThrowableWithExtraContext qt =
179           new RetriesExhaustedException.ThrowableWithExtraContext(t,
180             System.currentTimeMillis(), toString());
181         exceptions.add(qt);
182         if (tries == numRetries - 1) {
183           throw new RetriesExhaustedException(tries, exceptions);
184         }
185       } finally {
186         afterCall();
187       }
188       try {
189         Thread.sleep(ConnectionUtils.getPauseTime(pause, tries));
190       } catch (InterruptedException e) {
191         Thread.currentThread().interrupt();
192         throw new IOException("Giving up after tries=" + tries, e);
193       }
194     }
195     return null;
196   }
197 
198   /**
199    * Run this instance against the server once.
200    * @param <T> the type of the return value
201    * @return an object of type T
202    * @throws IOException if a remote or network exception occurs
203    * @throws RuntimeException other unspecified error
204    */
205   public T withoutRetries()
206   throws IOException, RuntimeException {
207     try {
208       beforeCall();
209       connect(false);
210       return call();
211     } catch (Throwable t) {
212       Throwable t2 = translateException(t);
213       if (t2 instanceof IOException) {
214         throw (IOException)t2;
215       } else {
216         throw new RuntimeException(t2);
217       }
218     } finally {
219       afterCall();
220     }
221   }
222 
223   private static Throwable translateException(Throwable t) throws IOException {
224     if (t instanceof UndeclaredThrowableException) {
225       t = t.getCause();
226     }
227     if (t instanceof RemoteException) {
228       t = ((RemoteException)t).unwrapRemoteException();
229     }
230     if (t instanceof DoNotRetryIOException) {
231       throw (DoNotRetryIOException)t;
232     }
233     return t;
234   }
235 }