View Javadoc

1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  package org.apache.hadoop.hbase.ipc;
19  
20  import com.google.protobuf.RpcCallback;
21  
22  import java.io.IOException;
23  import java.util.ArrayList;
24  import java.util.List;
25  
26  import org.apache.hadoop.hbase.CellScannable;
27  import org.apache.hadoop.hbase.CellScanner;
28  import org.apache.hadoop.hbase.CellUtil;
29  import org.apache.hadoop.hbase.HConstants;
30  import org.apache.hadoop.hbase.TableName;
31  import org.apache.hadoop.hbase.classification.InterfaceAudience;
32  
33  /**
34   * Optionally carries Cells across the proxy/service interface down into ipc. On its way out it
35   * optionally carries a set of result Cell data. We stick the Cells here when we want to avoid
36   * having to protobuf them (for performance reasons). This class is used ferrying data across the
37   * proxy/protobuf service chasm. Also does call timeout. Used by client and server ipc'ing.
38   */
39  @InterfaceAudience.Private
40  public class HBaseRpcControllerImpl implements HBaseRpcController {
41    /**
42     * The time, in ms before the call should expire.
43     */
44    private Integer callTimeout;
45  
46    private boolean done = false;
47  
48    private boolean cancelled = false;
49  
50    private final List<RpcCallback<Object>> cancellationCbs = new ArrayList<>();
51  
52    private IOException exception;
53  
54    /**
55     * Priority to set on this request. Set it here in controller so available composing the request.
56     * This is the ordained way of setting priorities going forward. We will be undoing the old
57     * annotation-based mechanism.
58     */
59    private int priority = HConstants.PRIORITY_UNSET;
60  
61    /**
62     * They are optionally set on construction, cleared after we make the call, and then optionally
63     * set on response with the result. We use this lowest common denominator access to Cells because
64     * sometimes the scanner is backed by a List of Cells and other times, it is backed by an encoded
65     * block that implements CellScanner.
66     */
67    private CellScanner cellScanner;
68  
69    public HBaseRpcControllerImpl() {
70      this((CellScanner) null);
71    }
72  
73    public HBaseRpcControllerImpl(final CellScanner cellScanner) {
74      this.cellScanner = cellScanner;
75    }
76  
77    public HBaseRpcControllerImpl(final List<CellScannable> cellIterables) {
78      this.cellScanner = cellIterables == null ? null : CellUtil.createCellScanner(cellIterables);
79    }
80  
81    /**
82     * @return One-shot cell scanner (you cannot back it up and restart)
83     */
84    @Override
85    public CellScanner cellScanner() {
86      return cellScanner;
87    }
88  
89    @edu.umd.cs.findbugs.annotations.SuppressWarnings(value = "IS2_INCONSISTENT_SYNC",
90        justification = "The only possible race method is startCancel")
91    @Override
92    public void setCellScanner(final CellScanner cellScanner) {
93      this.cellScanner = cellScanner;
94    }
95  
96    @Override
97    public void setPriority(int priority) {
98      this.priority = Math.max(this.priority, priority);
99    }
100 
101   @Override
102   public void setPriority(final TableName tn) {
103     setPriority(
104       tn != null && tn.isSystemTable() ? HConstants.SYSTEMTABLE_QOS : HConstants.NORMAL_QOS);
105   }
106 
107   @Override
108   public int getPriority() {
109     return priority < 0 ? HConstants.NORMAL_QOS : priority;
110   }
111 
112   @edu.umd.cs.findbugs.annotations.SuppressWarnings(value = "IS2_INCONSISTENT_SYNC",
113       justification = "The only possible race method is startCancel")
114   @Override
115   public void reset() {
116     priority = 0;
117     cellScanner = null;
118     exception = null;
119     callTimeout = null;
120     // In the implementations of some callable with replicas, rpc calls are executed in a executor
121     // and we could cancel the operation from outside which means there could be a race between
122     // reset and startCancel. Although I think the race should be handled by the callable since the
123     // reset may clear the cancel state...
124     synchronized (this) {
125       done = false;
126       cancelled = false;
127       cancellationCbs.clear();
128     }
129   }
130 
131   @Override
132   public int getCallTimeout() {
133     if (callTimeout != null) {
134       return callTimeout.intValue();
135     } else {
136       return 0;
137     }
138   }
139 
140   @Override
141   public void setCallTimeout(int callTimeout) {
142     this.callTimeout = callTimeout;
143   }
144 
145   @Override
146   public boolean hasCallTimeout() {
147     return callTimeout != null;
148   }
149 
150   @Override
151   public synchronized String errorText() {
152     if (!done || exception == null) {
153       return null;
154     }
155     return exception.getMessage();
156   }
157 
158   @Override
159   public synchronized boolean failed() {
160     return done && this.exception != null;
161   }
162 
163   @Override
164   public synchronized boolean isCanceled() {
165     return cancelled;
166   }
167 
168   @Override
169   public void notifyOnCancel(RpcCallback<Object> callback) {
170     synchronized (this) {
171       if (done) {
172         return;
173       }
174       if (!cancelled) {
175         cancellationCbs.add(callback);
176         return;
177       }
178     }
179     // run it directly as we have already been cancelled.
180     callback.run(null);
181   }
182 
183   @Override
184   public synchronized void setFailed(String reason) {
185     if (done) {
186       return;
187     }
188     done = true;
189     exception = new IOException(reason);
190   }
191 
192   @Override
193   public synchronized void setFailed(IOException e) {
194     if (done) {
195       return;
196     }
197     done = true;
198     exception = e;
199   }
200 
201   @Override
202   public synchronized IOException getFailed() {
203     return done ? exception : null;
204   }
205 
206   @Override
207   public synchronized void setDone(CellScanner cellScanner) {
208     if (done) {
209       return;
210     }
211     done = true;
212     this.cellScanner = cellScanner;
213   }
214 
215   @Override
216   public void startCancel() {
217     // As said above in the comment of reset, the cancellationCbs maybe cleared by reset, so we need
218     // to copy it.
219     List<RpcCallback<Object>> cbs;
220     synchronized (this) {
221       if (done) {
222         return;
223       }
224       done = true;
225       cancelled = true;
226       cbs = new ArrayList<>(cancellationCbs);
227     }
228     for (RpcCallback<?> cb : cbs) {
229       cb.run(null);
230     }
231   }
232 
233   @Override
234   public synchronized void notifyOnCancel(RpcCallback<Object> callback, CancellationCallback action)
235       throws IOException {
236     if (cancelled) {
237       action.run(true);
238     } else {
239       cancellationCbs.add(callback);
240       action.run(false);
241     }
242   }
243 
244 }