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 import java.util.Map;
25 import java.util.Map.Entry;
26
27 import org.apache.commons.logging.Log;
28 import org.apache.commons.logging.LogFactory;
29 import org.apache.hadoop.hbase.classification.InterfaceAudience;
30 import org.apache.hadoop.conf.Configuration;
31 import org.apache.hadoop.hbase.Cell;
32 import org.apache.hadoop.hbase.CellScanner;
33 import org.apache.hadoop.hbase.CellUtil;
34 import org.apache.hadoop.hbase.DoNotRetryIOException;
35 import org.apache.hadoop.hbase.HBaseIOException;
36 import org.apache.hadoop.hbase.HRegionInfo;
37 import org.apache.hadoop.hbase.HRegionLocation;
38 import org.apache.hadoop.hbase.NotServingRegionException;
39 import org.apache.hadoop.hbase.RegionLocations;
40 import org.apache.hadoop.hbase.RemoteExceptionHandler;
41 import org.apache.hadoop.hbase.ServerName;
42 import org.apache.hadoop.hbase.TableName;
43 import org.apache.hadoop.hbase.UnknownScannerException;
44 import org.apache.hadoop.hbase.client.metrics.ScanMetrics;
45 import org.apache.hadoop.hbase.exceptions.ScannerResetException;
46 import org.apache.hadoop.hbase.ipc.PayloadCarryingRpcController;
47 import org.apache.hadoop.hbase.ipc.RpcControllerFactory;
48 import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
49 import org.apache.hadoop.hbase.protobuf.RequestConverter;
50 import org.apache.hadoop.hbase.protobuf.ResponseConverter;
51 import org.apache.hadoop.hbase.protobuf.generated.ClientProtos.ScanRequest;
52 import org.apache.hadoop.hbase.protobuf.generated.ClientProtos.ScanResponse;
53 import org.apache.hadoop.hbase.regionserver.RegionServerStoppedException;
54 import org.apache.hadoop.ipc.RemoteException;
55 import org.apache.hadoop.net.DNS;
56
57 import com.google.protobuf.ServiceException;
58 import com.google.protobuf.TextFormat;
59
60
61
62
63
64
65 @InterfaceAudience.Private
66 public class ScannerCallable extends RegionServerCallable<Result[]> {
67 public static final String LOG_SCANNER_LATENCY_CUTOFF
68 = "hbase.client.log.scanner.latency.cutoff";
69 public static final String LOG_SCANNER_ACTIVITY = "hbase.client.log.scanner.activity";
70
71
72 public static final Log LOG = LogFactory.getLog(ScannerCallable.class);
73 protected long scannerId = -1L;
74 protected boolean instantiated = false;
75 protected boolean closed = false;
76 protected boolean renew = false;
77 private Scan scan;
78 private int caching = 1;
79 protected final ClusterConnection cConnection;
80 protected ScanMetrics scanMetrics;
81 private boolean logScannerActivity = false;
82 private int logCutOffLatency = 1000;
83 private static String myAddress;
84 protected final int id;
85 protected boolean serverHasMoreResultsContext;
86 protected boolean serverHasMoreResults;
87
88
89
90
91
92 protected boolean heartbeatMessage = false;
93 static {
94 try {
95 myAddress = DNS.getDefaultHost("default", "default");
96 } catch (UnknownHostException uhe) {
97 LOG.error("cannot determine my address", uhe);
98 }
99 }
100
101
102 protected boolean isRegionServerRemote = true;
103 private long nextCallSeq = 0;
104 protected RpcControllerFactory controllerFactory;
105 protected PayloadCarryingRpcController controller;
106
107
108
109
110
111
112
113
114
115
116 public ScannerCallable (ClusterConnection connection, TableName tableName, Scan scan,
117 ScanMetrics scanMetrics, RpcControllerFactory rpcControllerFactory) {
118 this(connection, tableName, scan, scanMetrics, rpcControllerFactory, 0);
119 }
120
121
122
123
124
125
126
127
128 public ScannerCallable (ClusterConnection connection, TableName tableName, Scan scan,
129 ScanMetrics scanMetrics, RpcControllerFactory rpcControllerFactory, int id) {
130 super(connection, tableName, scan.getStartRow());
131 this.id = id;
132 this.cConnection = connection;
133 this.scan = scan;
134 this.scanMetrics = scanMetrics;
135 Configuration conf = connection.getConfiguration();
136 logScannerActivity = conf.getBoolean(LOG_SCANNER_ACTIVITY, false);
137 logCutOffLatency = conf.getInt(LOG_SCANNER_LATENCY_CUTOFF, 1000);
138 this.controllerFactory = rpcControllerFactory;
139 }
140
141 PayloadCarryingRpcController getController() {
142 return controller;
143 }
144
145
146
147
148
149 @Override
150 public void prepare(boolean reload) throws IOException {
151 if (Thread.interrupted()) {
152 throw new InterruptedIOException();
153 }
154 RegionLocations rl = RpcRetryingCallerWithReadReplicas.getRegionLocations(!reload,
155 id, getConnection(), getTableName(), getRow());
156 location = id < rl.size() ? rl.getRegionLocation(id) : null;
157 if (location == null || location.getServerName() == null) {
158
159
160 throw new HBaseIOException("There is no location for replica id #" + id);
161 }
162 ServerName dest = location.getServerName();
163 setStub(super.getConnection().getClient(dest));
164 if (!instantiated || reload) {
165 checkIfRegionServerIsRemote();
166 instantiated = true;
167 }
168
169
170
171
172 if (reload && this.scanMetrics != null) {
173 this.scanMetrics.countOfRPCRetries.incrementAndGet();
174 if (isRegionServerRemote) {
175 this.scanMetrics.countOfRemoteRPCRetries.incrementAndGet();
176 }
177 }
178 }
179
180
181
182
183
184 protected void checkIfRegionServerIsRemote() {
185 if (getLocation().getHostname().equalsIgnoreCase(myAddress)) {
186 isRegionServerRemote = false;
187 } else {
188 isRegionServerRemote = true;
189 }
190 }
191
192
193 @Override
194 public Result [] call(int callTimeout) throws IOException {
195 if (Thread.interrupted()) {
196 throw new InterruptedIOException();
197 }
198
199 if (controller == null) {
200 controller = controllerFactory.newController();
201 controller.setPriority(getTableName());
202 controller.setCallTimeout(callTimeout);
203 }
204
205 if (closed) {
206 if (scannerId != -1) {
207 close();
208 }
209 } else {
210 if (scannerId == -1L) {
211 this.scannerId = openScanner();
212 } else {
213 Result [] rrs = null;
214 ScanRequest request = null;
215
216 setHeartbeatMessage(false);
217 try {
218 incRPCcallsMetrics();
219 request =
220 RequestConverter.buildScanRequest(scannerId, caching, false, nextCallSeq,
221 this.scanMetrics != null, renew);
222 ScanResponse response = null;
223 try {
224 response = getStub().scan(controller, request);
225
226
227
228
229
230
231
232
233
234 nextCallSeq++;
235 long timestamp = System.currentTimeMillis();
236 setHeartbeatMessage(response.hasHeartbeatMessage() && response.getHeartbeatMessage());
237
238 CellScanner cellScanner = controller.cellScanner();
239 rrs = ResponseConverter.getResults(cellScanner, response);
240 if (logScannerActivity) {
241 long now = System.currentTimeMillis();
242 if (now - timestamp > logCutOffLatency) {
243 int rows = rrs == null ? 0 : rrs.length;
244 LOG.info("Took " + (now-timestamp) + "ms to fetch "
245 + rows + " rows from scanner=" + scannerId);
246 }
247 }
248 updateServerSideMetrics(response);
249
250 if (response.hasMoreResults() && !response.getMoreResults()) {
251 scannerId = -1L;
252 closed = true;
253
254 return null;
255 }
256
257
258 if (response.hasMoreResultsInRegion()) {
259
260 setHasMoreResultsContext(true);
261 setServerHasMoreResults(response.getMoreResultsInRegion());
262 } else {
263
264 setHasMoreResultsContext(false);
265 }
266 } catch (ServiceException se) {
267 throw ProtobufUtil.getRemoteException(se);
268 }
269 updateResultsMetrics(rrs);
270 } catch (IOException e) {
271 if (logScannerActivity) {
272 LOG.info("Got exception making request " + TextFormat.shortDebugString(request)
273 + " to " + getLocation(), e);
274 }
275 IOException ioe = e;
276 if (e instanceof RemoteException) {
277 ioe = RemoteExceptionHandler.decodeRemoteException((RemoteException)e);
278 }
279 if (logScannerActivity) {
280 if (ioe instanceof UnknownScannerException) {
281 try {
282 HRegionLocation location =
283 getConnection().relocateRegion(getTableName(), scan.getStartRow());
284 LOG.info("Scanner=" + scannerId
285 + " expired, current region location is " + location.toString());
286 } catch (Throwable t) {
287 LOG.info("Failed to relocate region", t);
288 }
289 } else if (ioe instanceof ScannerResetException) {
290 LOG.info("Scanner=" + scannerId + " has received an exception, and the server "
291 + "asked us to reset the scanner state.", ioe);
292 }
293 }
294
295
296
297
298
299
300 if (ioe instanceof NotServingRegionException) {
301
302
303
304 if (this.scanMetrics != null) {
305 this.scanMetrics.countOfNSRE.incrementAndGet();
306 }
307 throw new DoNotRetryIOException("Resetting the scanner -- see exception cause", ioe);
308 } else if (ioe instanceof RegionServerStoppedException) {
309
310
311 throw new DoNotRetryIOException("Resetting the scanner -- see exception cause", ioe);
312 } else {
313
314 throw ioe;
315 }
316 }
317 return rrs;
318 }
319 }
320 return null;
321 }
322
323
324
325
326
327
328
329 protected boolean isHeartbeatMessage() {
330 return heartbeatMessage;
331 }
332
333 protected void setHeartbeatMessage(boolean heartbeatMessage) {
334 this.heartbeatMessage = heartbeatMessage;
335 }
336
337 private void incRPCcallsMetrics() {
338 if (this.scanMetrics == null) {
339 return;
340 }
341 this.scanMetrics.countOfRPCcalls.incrementAndGet();
342 if (isRegionServerRemote) {
343 this.scanMetrics.countOfRemoteRPCcalls.incrementAndGet();
344 }
345 }
346
347 protected void updateResultsMetrics(Result[] rrs) {
348 if (this.scanMetrics == null || rrs == null || rrs.length == 0) {
349 return;
350 }
351 long resultSize = 0;
352 for (Result rr : rrs) {
353 for (Cell cell : rr.rawCells()) {
354 resultSize += CellUtil.estimatedSerializedSizeOf(cell);
355 }
356 }
357 this.scanMetrics.countOfBytesInResults.addAndGet(resultSize);
358 if (isRegionServerRemote) {
359 this.scanMetrics.countOfBytesInRemoteResults.addAndGet(resultSize);
360 }
361 }
362
363
364
365
366
367
368
369 private void updateServerSideMetrics(ScanResponse response) {
370 if (this.scanMetrics == null || response == null || !response.hasScanMetrics()) return;
371
372 Map<String, Long> serverMetrics = ResponseConverter.getScanMetrics(response);
373 for (Entry<String, Long> entry : serverMetrics.entrySet()) {
374 this.scanMetrics.addToCounter(entry.getKey(), entry.getValue());
375 }
376 }
377
378 private void close() {
379 if (this.scannerId == -1L) {
380 return;
381 }
382 try {
383 incRPCcallsMetrics();
384 ScanRequest request =
385 RequestConverter.buildScanRequest(this.scannerId, 0, true, this.scanMetrics != null);
386 try {
387 getStub().scan(controller, request);
388 } catch (ServiceException se) {
389 throw ProtobufUtil.getRemoteException(se);
390 }
391 } catch (IOException e) {
392 LOG.warn("Ignore, probably already closed", e);
393 }
394 this.scannerId = -1L;
395 }
396
397 protected long openScanner() throws IOException {
398 incRPCcallsMetrics();
399 ScanRequest request =
400 RequestConverter.buildScanRequest(
401 getLocation().getRegionInfo().getRegionName(),
402 this.scan, 0, false);
403 try {
404 ScanResponse response = getStub().scan(controller, request);
405 long id = response.getScannerId();
406 if (logScannerActivity) {
407 LOG.info("Open scanner=" + id + " for scan=" + scan.toString()
408 + " on region " + getLocation().toString());
409 }
410 return id;
411 } catch (ServiceException se) {
412 throw ProtobufUtil.getRemoteException(se);
413 }
414 }
415
416 protected Scan getScan() {
417 return scan;
418 }
419
420
421
422
423 public void setClose() {
424 this.closed = true;
425 }
426
427
428
429
430
431
432 public void setRenew(boolean val) {
433 this.renew = val;
434 }
435
436
437
438
439 @Override
440 public HRegionInfo getHRegionInfo() {
441 if (!instantiated) {
442 return null;
443 }
444 return getLocation().getRegionInfo();
445 }
446
447
448
449
450
451 public int getCaching() {
452 return caching;
453 }
454
455 @Override
456 public ClusterConnection getConnection() {
457 return cConnection;
458 }
459
460
461
462
463
464 public void setCaching(int caching) {
465 this.caching = caching;
466 }
467
468 public ScannerCallable getScannerCallableForReplica(int id) {
469 ScannerCallable s = new ScannerCallable(this.getConnection(), this.tableName,
470 this.getScan(), this.scanMetrics, controllerFactory, id);
471 s.setCaching(this.caching);
472 return s;
473 }
474
475
476
477
478
479 protected boolean getServerHasMoreResults() {
480 assert serverHasMoreResultsContext;
481 return this.serverHasMoreResults;
482 }
483
484 protected void setServerHasMoreResults(boolean serverHasMoreResults) {
485 this.serverHasMoreResults = serverHasMoreResults;
486 }
487
488
489
490
491
492
493 protected boolean hasMoreResultsContext() {
494 return serverHasMoreResultsContext;
495 }
496
497 protected void setHasMoreResultsContext(boolean serverHasMoreResultsContext) {
498 this.serverHasMoreResultsContext = serverHasMoreResultsContext;
499 }
500 }