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