1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.hadoop.hbase.client;
20
21 import java.io.IOException;
22 import java.io.InterruptedIOException;
23 import java.net.UnknownHostException;
24
25 import org.apache.commons.logging.Log;
26 import org.apache.commons.logging.LogFactory;
27 import org.apache.hadoop.hbase.classification.InterfaceAudience;
28 import org.apache.hadoop.conf.Configuration;
29 import org.apache.hadoop.hbase.Cell;
30 import org.apache.hadoop.hbase.CellScanner;
31 import org.apache.hadoop.hbase.CellUtil;
32 import org.apache.hadoop.hbase.DoNotRetryIOException;
33 import org.apache.hadoop.hbase.HBaseIOException;
34 import org.apache.hadoop.hbase.HRegionInfo;
35 import org.apache.hadoop.hbase.HRegionLocation;
36 import org.apache.hadoop.hbase.NotServingRegionException;
37 import org.apache.hadoop.hbase.RegionLocations;
38 import org.apache.hadoop.hbase.RemoteExceptionHandler;
39 import org.apache.hadoop.hbase.ServerName;
40 import org.apache.hadoop.hbase.TableName;
41 import org.apache.hadoop.hbase.UnknownScannerException;
42 import org.apache.hadoop.hbase.client.metrics.ScanMetrics;
43 import org.apache.hadoop.hbase.ipc.PayloadCarryingRpcController;
44 import org.apache.hadoop.hbase.ipc.RpcControllerFactory;
45 import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
46 import org.apache.hadoop.hbase.protobuf.RequestConverter;
47 import org.apache.hadoop.hbase.protobuf.ResponseConverter;
48 import org.apache.hadoop.hbase.protobuf.generated.ClientProtos.ScanRequest;
49 import org.apache.hadoop.hbase.protobuf.generated.ClientProtos.ScanResponse;
50 import org.apache.hadoop.hbase.regionserver.RegionServerStoppedException;
51 import org.apache.hadoop.ipc.RemoteException;
52 import org.apache.hadoop.net.DNS;
53
54 import com.google.protobuf.ServiceException;
55 import com.google.protobuf.TextFormat;
56
57
58
59
60
61
62 @InterfaceAudience.Private
63 public class ScannerCallable extends RegionServerCallable<Result[]> {
64 public static final String LOG_SCANNER_LATENCY_CUTOFF
65 = "hbase.client.log.scanner.latency.cutoff";
66 public static final String LOG_SCANNER_ACTIVITY = "hbase.client.log.scanner.activity";
67
68 public static final Log LOG = LogFactory.getLog(ScannerCallable.class);
69 protected long scannerId = -1L;
70 protected boolean instantiated = false;
71 protected boolean closed = false;
72 private Scan scan;
73 private int caching = 1;
74 protected final ClusterConnection cConnection;
75 protected ScanMetrics scanMetrics;
76 private boolean logScannerActivity = false;
77 private int logCutOffLatency = 1000;
78 private static String myAddress;
79 protected final int id;
80 static {
81 try {
82 myAddress = DNS.getDefaultHost("default", "default");
83 } catch (UnknownHostException uhe) {
84 LOG.error("cannot determine my address", uhe);
85 }
86 }
87
88
89 protected boolean isRegionServerRemote = true;
90 private long nextCallSeq = 0;
91 protected RpcControllerFactory controllerFactory;
92
93
94
95
96
97
98
99
100
101
102 public ScannerCallable (ClusterConnection connection, TableName tableName, Scan scan,
103 ScanMetrics scanMetrics, RpcControllerFactory rpcControllerFactory) {
104 this(connection, tableName, scan, scanMetrics, rpcControllerFactory, 0);
105 }
106
107
108
109
110
111
112
113
114 public ScannerCallable (ClusterConnection connection, TableName tableName, Scan scan,
115 ScanMetrics scanMetrics, RpcControllerFactory rpcControllerFactory, int id) {
116 super(connection, tableName, scan.getStartRow());
117 this.id = id;
118 this.cConnection = connection;
119 this.scan = scan;
120 this.scanMetrics = scanMetrics;
121 Configuration conf = connection.getConfiguration();
122 logScannerActivity = conf.getBoolean(LOG_SCANNER_ACTIVITY, false);
123 logCutOffLatency = conf.getInt(LOG_SCANNER_LATENCY_CUTOFF, 1000);
124 this.controllerFactory = rpcControllerFactory;
125 }
126
127
128
129
130
131 @Override
132 public void prepare(boolean reload) throws IOException {
133 if (Thread.interrupted()) {
134 throw new InterruptedIOException();
135 }
136 RegionLocations rl = RpcRetryingCallerWithReadReplicas.getRegionLocations(!reload,
137 id, getConnection(), getTableName(), getRow());
138 location = id < rl.size() ? rl.getRegionLocation(id) : null;
139 if (location == null || location.getServerName() == null) {
140
141
142 throw new HBaseIOException("There is no location for replica id #" + id);
143 }
144 ServerName dest = location.getServerName();
145 setStub(super.getConnection().getClient(dest));
146 if (!instantiated || reload) {
147 checkIfRegionServerIsRemote();
148 instantiated = true;
149 }
150
151
152
153
154 if (reload && this.scanMetrics != null) {
155 this.scanMetrics.countOfRPCRetries.incrementAndGet();
156 if (isRegionServerRemote) {
157 this.scanMetrics.countOfRemoteRPCRetries.incrementAndGet();
158 }
159 }
160 }
161
162
163
164
165
166 protected void checkIfRegionServerIsRemote() {
167 if (getLocation().getHostname().equalsIgnoreCase(myAddress)) {
168 isRegionServerRemote = false;
169 } else {
170 isRegionServerRemote = true;
171 }
172 }
173
174
175 @Override
176 @SuppressWarnings("deprecation")
177 public Result [] call(int callTimeout) throws IOException {
178 if (Thread.interrupted()) {
179 throw new InterruptedIOException();
180 }
181 if (closed) {
182 if (scannerId != -1) {
183 close();
184 }
185 } else {
186 if (scannerId == -1L) {
187 this.scannerId = openScanner();
188 } else {
189 Result [] rrs = null;
190 ScanRequest request = null;
191 try {
192 incRPCcallsMetrics();
193 request = RequestConverter.buildScanRequest(scannerId, caching, false, nextCallSeq);
194 ScanResponse response = null;
195 PayloadCarryingRpcController controller = controllerFactory.newController();
196 controller.setPriority(getTableName());
197 controller.setCallTimeout(callTimeout);
198 try {
199 response = getStub().scan(controller, request);
200
201
202
203
204
205
206
207
208
209 nextCallSeq++;
210 long timestamp = System.currentTimeMillis();
211
212 CellScanner cellScanner = controller.cellScanner();
213 rrs = ResponseConverter.getResults(cellScanner, response);
214 if (logScannerActivity) {
215 long now = System.currentTimeMillis();
216 if (now - timestamp > logCutOffLatency) {
217 int rows = rrs == null ? 0 : rrs.length;
218 LOG.info("Took " + (now-timestamp) + "ms to fetch "
219 + rows + " rows from scanner=" + scannerId);
220 }
221 }
222 if (response.hasMoreResults()
223 && !response.getMoreResults()) {
224 scannerId = -1L;
225 closed = true;
226 return null;
227 }
228 } catch (ServiceException se) {
229 throw ProtobufUtil.getRemoteException(se);
230 }
231 updateResultsMetrics(rrs);
232 } catch (IOException e) {
233 if (logScannerActivity) {
234 LOG.info("Got exception making request " + TextFormat.shortDebugString(request)
235 + " to " + getLocation(), e);
236 }
237 IOException ioe = e;
238 if (e instanceof RemoteException) {
239 ioe = RemoteExceptionHandler.decodeRemoteException((RemoteException)e);
240 }
241 if (logScannerActivity && (ioe instanceof UnknownScannerException)) {
242 try {
243 HRegionLocation location =
244 getConnection().relocateRegion(getTableName(), scan.getStartRow());
245 LOG.info("Scanner=" + scannerId
246 + " expired, current region location is " + location.toString());
247 } catch (Throwable t) {
248 LOG.info("Failed to relocate region", t);
249 }
250 }
251
252
253
254
255
256
257 if (ioe instanceof NotServingRegionException) {
258
259
260
261 if (this.scanMetrics != null) {
262 this.scanMetrics.countOfNSRE.incrementAndGet();
263 }
264 throw new DoNotRetryIOException("Resetting the scanner -- see exception cause", ioe);
265 } else if (ioe instanceof RegionServerStoppedException) {
266
267
268 throw new DoNotRetryIOException("Resetting the scanner -- see exception cause", ioe);
269 } else {
270
271 throw ioe;
272 }
273 }
274 return rrs;
275 }
276 }
277 return null;
278 }
279
280 private void incRPCcallsMetrics() {
281 if (this.scanMetrics == null) {
282 return;
283 }
284 this.scanMetrics.countOfRPCcalls.incrementAndGet();
285 if (isRegionServerRemote) {
286 this.scanMetrics.countOfRemoteRPCcalls.incrementAndGet();
287 }
288 }
289
290 private void updateResultsMetrics(Result[] rrs) {
291 if (this.scanMetrics == null || rrs == null || rrs.length == 0) {
292 return;
293 }
294 long resultSize = 0;
295 for (Result rr : rrs) {
296 for (Cell cell : rr.rawCells()) {
297 resultSize += CellUtil.estimatedSerializedSizeOf(cell);
298 }
299 }
300 this.scanMetrics.countOfBytesInResults.addAndGet(resultSize);
301 if (isRegionServerRemote) {
302 this.scanMetrics.countOfBytesInRemoteResults.addAndGet(resultSize);
303 }
304 }
305
306 private void close() {
307 if (this.scannerId == -1L) {
308 return;
309 }
310 try {
311 incRPCcallsMetrics();
312 ScanRequest request =
313 RequestConverter.buildScanRequest(this.scannerId, 0, true);
314 try {
315 getStub().scan(null, request);
316 } catch (ServiceException se) {
317 throw ProtobufUtil.getRemoteException(se);
318 }
319 } catch (IOException e) {
320 LOG.warn("Ignore, probably already closed", e);
321 }
322 this.scannerId = -1L;
323 }
324
325 protected long openScanner() throws IOException {
326 incRPCcallsMetrics();
327 ScanRequest request =
328 RequestConverter.buildScanRequest(
329 getLocation().getRegionInfo().getRegionName(),
330 this.scan, 0, false);
331 try {
332 ScanResponse response = getStub().scan(null, request);
333 long id = response.getScannerId();
334 if (logScannerActivity) {
335 LOG.info("Open scanner=" + id + " for scan=" + scan.toString()
336 + " on region " + getLocation().toString());
337 }
338 return id;
339 } catch (ServiceException se) {
340 throw ProtobufUtil.getRemoteException(se);
341 }
342 }
343
344 protected Scan getScan() {
345 return scan;
346 }
347
348
349
350
351 public void setClose() {
352 this.closed = true;
353 }
354
355
356
357
358 @Override
359 public HRegionInfo getHRegionInfo() {
360 if (!instantiated) {
361 return null;
362 }
363 return getLocation().getRegionInfo();
364 }
365
366
367
368
369
370 public int getCaching() {
371 return caching;
372 }
373
374 @Override
375 public ClusterConnection getConnection() {
376 return cConnection;
377 }
378
379
380
381
382
383 public void setCaching(int caching) {
384 this.caching = caching;
385 }
386
387 public ScannerCallable getScannerCallableForReplica(int id) {
388 ScannerCallable s = new ScannerCallable(this.getConnection(), this.tableName,
389 this.getScan(), this.scanMetrics, controllerFactory, id);
390 s.setCaching(this.caching);
391 return s;
392 }
393 }