1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.hadoop.hbase.client;
19
20 import java.io.IOException;
21 import java.io.InterruptedIOException;
22 import java.util.LinkedList;
23 import java.util.concurrent.ExecutorService;
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.CellUtil;
31 import org.apache.hadoop.hbase.DoNotRetryIOException;
32 import org.apache.hadoop.hbase.HBaseConfiguration;
33 import org.apache.hadoop.hbase.HConstants;
34 import org.apache.hadoop.hbase.HRegionInfo;
35 import org.apache.hadoop.hbase.NotServingRegionException;
36 import org.apache.hadoop.hbase.TableName;
37 import org.apache.hadoop.hbase.UnknownScannerException;
38 import org.apache.hadoop.hbase.client.RpcRetryingCallerFactory;
39 import org.apache.hadoop.hbase.exceptions.OutOfOrderScannerNextException;
40 import org.apache.hadoop.hbase.ipc.RpcControllerFactory;
41 import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
42 import org.apache.hadoop.hbase.protobuf.generated.MapReduceProtos;
43 import org.apache.hadoop.hbase.regionserver.RegionServerStoppedException;
44 import org.apache.hadoop.hbase.util.Bytes;
45
46
47
48
49
50
51 @InterfaceAudience.Private
52 public class ClientScanner extends AbstractClientScanner {
53 private final Log LOG = LogFactory.getLog(this.getClass());
54 protected Scan scan;
55 protected boolean closed = false;
56
57
58 protected HRegionInfo currentRegion = null;
59 protected ScannerCallableWithReplicas callable = null;
60 protected final LinkedList<Result> cache = new LinkedList<Result>();
61 protected final int caching;
62 protected long lastNext;
63
64 protected Result lastResult = null;
65 protected final long maxScannerResultSize;
66 private final ClusterConnection connection;
67 private final TableName tableName;
68 protected final int scannerTimeout;
69 protected boolean scanMetricsPublished = false;
70 protected RpcRetryingCaller<Result []> caller;
71 protected RpcControllerFactory rpcControllerFactory;
72 protected Configuration conf;
73
74
75
76
77
78 protected final int primaryOperationTimeout;
79 private int retries;
80 protected final ExecutorService pool;
81
82
83
84
85
86
87
88
89
90
91 public ClientScanner(final Configuration conf, final Scan scan, final TableName tableName,
92 ClusterConnection connection, RpcRetryingCallerFactory rpcFactory,
93 RpcControllerFactory controllerFactory, ExecutorService pool, int primaryOperationTimeout) throws IOException {
94 if (LOG.isTraceEnabled()) {
95 LOG.trace("Scan table=" + tableName
96 + ", startRow=" + Bytes.toStringBinary(scan.getStartRow()));
97 }
98 this.scan = scan;
99 this.tableName = tableName;
100 this.lastNext = System.currentTimeMillis();
101 this.connection = connection;
102 this.pool = pool;
103 this.primaryOperationTimeout = primaryOperationTimeout;
104 this.retries = conf.getInt(HConstants.HBASE_CLIENT_RETRIES_NUMBER,
105 HConstants.DEFAULT_HBASE_CLIENT_RETRIES_NUMBER);
106 if (scan.getMaxResultSize() > 0) {
107 this.maxScannerResultSize = scan.getMaxResultSize();
108 } else {
109 this.maxScannerResultSize = conf.getLong(
110 HConstants.HBASE_CLIENT_SCANNER_MAX_RESULT_SIZE_KEY,
111 HConstants.DEFAULT_HBASE_CLIENT_SCANNER_MAX_RESULT_SIZE);
112 }
113 this.scannerTimeout = HBaseConfiguration.getInt(conf,
114 HConstants.HBASE_CLIENT_SCANNER_TIMEOUT_PERIOD,
115 HConstants.HBASE_REGIONSERVER_LEASE_PERIOD_KEY,
116 HConstants.DEFAULT_HBASE_CLIENT_SCANNER_TIMEOUT_PERIOD);
117
118
119 initScanMetrics(scan);
120
121
122 if (this.scan.getCaching() > 0) {
123 this.caching = this.scan.getCaching();
124 } else {
125 this.caching = conf.getInt(
126 HConstants.HBASE_CLIENT_SCANNER_CACHING,
127 HConstants.DEFAULT_HBASE_CLIENT_SCANNER_CACHING);
128 }
129
130 this.caller = rpcFactory.<Result[]> newCaller();
131 this.rpcControllerFactory = controllerFactory;
132
133 this.conf = conf;
134 initializeScannerInConstruction();
135 }
136
137 protected void initializeScannerInConstruction() throws IOException{
138
139 nextScanner(this.caching, false);
140 }
141
142 protected ClusterConnection getConnection() {
143 return this.connection;
144 }
145
146
147
148
149
150 @Deprecated
151 protected byte [] getTableName() {
152 return this.tableName.getName();
153 }
154
155 protected TableName getTable() {
156 return this.tableName;
157 }
158
159 protected int getRetries() {
160 return this.retries;
161 }
162
163 protected int getScannerTimeout() {
164 return this.scannerTimeout;
165 }
166
167 protected Configuration getConf() {
168 return this.conf;
169 }
170
171 protected Scan getScan() {
172 return scan;
173 }
174
175 protected ExecutorService getPool() {
176 return pool;
177 }
178
179 protected int getPrimaryOperationTimeout() {
180 return primaryOperationTimeout;
181 }
182
183 protected int getCaching() {
184 return caching;
185 }
186
187 protected long getTimestamp() {
188 return lastNext;
189 }
190
191
192 protected boolean checkScanStopRow(final byte [] endKey) {
193 if (this.scan.getStopRow().length > 0) {
194
195 byte [] stopRow = scan.getStopRow();
196 int cmp = Bytes.compareTo(stopRow, 0, stopRow.length,
197 endKey, 0, endKey.length);
198 if (cmp <= 0) {
199
200
201 return true;
202 }
203 }
204 return false;
205 }
206
207 private boolean possiblyNextScanner(int nbRows, final boolean done) throws IOException {
208
209
210
211
212 if (callable != null && callable.switchedToADifferentReplica()) return true;
213 return nextScanner(nbRows, done);
214 }
215
216
217
218
219
220
221
222
223
224
225 protected boolean nextScanner(int nbRows, final boolean done)
226 throws IOException {
227
228 if (this.callable != null) {
229 this.callable.setClose();
230 call(scan, callable, caller, scannerTimeout);
231 this.callable = null;
232 }
233
234
235 byte [] localStartKey;
236
237
238 if (this.currentRegion != null) {
239 byte [] endKey = this.currentRegion.getEndKey();
240 if (endKey == null ||
241 Bytes.equals(endKey, HConstants.EMPTY_BYTE_ARRAY) ||
242 checkScanStopRow(endKey) ||
243 done) {
244 close();
245 if (LOG.isTraceEnabled()) {
246 LOG.trace("Finished " + this.currentRegion);
247 }
248 return false;
249 }
250 localStartKey = endKey;
251 if (LOG.isTraceEnabled()) {
252 LOG.trace("Finished " + this.currentRegion);
253 }
254 } else {
255 localStartKey = this.scan.getStartRow();
256 }
257
258 if (LOG.isDebugEnabled() && this.currentRegion != null) {
259
260 LOG.debug("Advancing internal scanner to startKey at '" +
261 Bytes.toStringBinary(localStartKey) + "'");
262 }
263 try {
264 callable = getScannerCallable(localStartKey, nbRows);
265
266
267 call(scan, callable, caller, scannerTimeout);
268 this.currentRegion = callable.getHRegionInfo();
269 if (this.scanMetrics != null) {
270 this.scanMetrics.countOfRegions.incrementAndGet();
271 }
272 } catch (IOException e) {
273 close();
274 throw e;
275 }
276 return true;
277 }
278
279
280 Result[] call(Scan scan, ScannerCallableWithReplicas callable,
281 RpcRetryingCaller<Result[]> caller, int scannerTimeout)
282 throws IOException, RuntimeException {
283 if (Thread.interrupted()) {
284 throw new InterruptedIOException();
285 }
286
287
288 return caller.callWithoutRetries(callable, scannerTimeout);
289 }
290
291 @InterfaceAudience.Private
292 protected ScannerCallableWithReplicas getScannerCallable(byte [] localStartKey,
293 int nbRows) {
294 scan.setStartRow(localStartKey);
295 ScannerCallable s =
296 new ScannerCallable(getConnection(), getTable(), scan, this.scanMetrics,
297 this.rpcControllerFactory);
298 s.setCaching(nbRows);
299 ScannerCallableWithReplicas sr = new ScannerCallableWithReplicas(tableName, getConnection(),
300 s, pool, primaryOperationTimeout, scan,
301 retries, scannerTimeout, caching, conf, caller);
302 return sr;
303 }
304
305
306
307
308
309
310
311
312
313
314
315
316 protected void writeScanMetrics() {
317 if (this.scanMetrics == null || scanMetricsPublished) {
318 return;
319 }
320 MapReduceProtos.ScanMetrics pScanMetrics = ProtobufUtil.toScanMetrics(scanMetrics);
321 scan.setAttribute(Scan.SCAN_ATTRIBUTES_METRICS_DATA, pScanMetrics.toByteArray());
322 scanMetricsPublished = true;
323 }
324
325 @Override
326 public Result next() throws IOException {
327
328 if (cache.size() == 0 && this.closed) {
329 return null;
330 }
331 if (cache.size() == 0) {
332 loadCache();
333 }
334
335 if (cache.size() > 0) {
336 return cache.poll();
337 }
338
339
340 writeScanMetrics();
341 return null;
342 }
343
344
345
346
347 protected void loadCache() throws IOException {
348 Result[] values = null;
349 long remainingResultSize = maxScannerResultSize;
350 int countdown = this.caching;
351
352
353
354 callable.setCaching(this.caching);
355
356
357 boolean skipFirst = false;
358 boolean retryAfterOutOfOrderException = true;
359
360
361 boolean serverHasMoreResults = false;
362 do {
363 try {
364 if (skipFirst) {
365
366
367 callable.setCaching(1);
368 values = call(scan, callable, caller, scannerTimeout);
369
370
371
372
373
374
375 if (values == null && callable.switchedToADifferentReplica()) {
376 if (this.lastResult != null) {
377 skipFirst = true;
378 }
379 this.currentRegion = callable.getHRegionInfo();
380 continue;
381 }
382 callable.setCaching(this.caching);
383 skipFirst = false;
384 }
385
386
387
388 values = call(scan, callable, caller, scannerTimeout);
389 if (skipFirst && values != null && values.length == 1) {
390 skipFirst = false;
391 values = call(scan, callable, caller, scannerTimeout);
392 }
393
394
395
396
397
398
399 if (values == null && callable.switchedToADifferentReplica()) {
400 if (this.lastResult != null) {
401 skipFirst = true;
402 }
403 this.currentRegion = callable.getHRegionInfo();
404 continue;
405 }
406 retryAfterOutOfOrderException = true;
407 } catch (DoNotRetryIOException e) {
408
409
410 if (e instanceof UnknownScannerException) {
411 long timeout = lastNext + scannerTimeout;
412
413
414
415 if (timeout < System.currentTimeMillis()) {
416 long elapsed = System.currentTimeMillis() - lastNext;
417 ScannerTimeoutException ex =
418 new ScannerTimeoutException(elapsed + "ms passed since the last invocation, "
419 + "timeout is currently set to " + scannerTimeout);
420 ex.initCause(e);
421 throw ex;
422 }
423 } else {
424
425
426 Throwable cause = e.getCause();
427 if ((cause != null && cause instanceof NotServingRegionException) ||
428 (cause != null && cause instanceof RegionServerStoppedException) ||
429 e instanceof OutOfOrderScannerNextException) {
430
431
432
433 } else {
434 throw e;
435 }
436 }
437
438 if (this.lastResult != null) {
439
440
441
442
443
444
445 this.scan.setStartRow(this.lastResult.getRow());
446
447
448
449 skipFirst = true;
450 }
451 if (e instanceof OutOfOrderScannerNextException) {
452 if (retryAfterOutOfOrderException) {
453 retryAfterOutOfOrderException = false;
454 } else {
455
456 throw new DoNotRetryIOException("Failed after retry of " +
457 "OutOfOrderScannerNextException: was there a rpc timeout?", e);
458 }
459 }
460
461 this.currentRegion = null;
462
463
464 callable = null;
465
466 continue;
467 }
468 long currentTime = System.currentTimeMillis();
469 if (this.scanMetrics != null) {
470 this.scanMetrics.sumOfMillisSecBetweenNexts.addAndGet(currentTime - lastNext);
471 }
472 lastNext = currentTime;
473 if (values != null && values.length > 0) {
474 for (Result rs : values) {
475 cache.add(rs);
476
477 for (Cell cell : rs.rawCells()) {
478 remainingResultSize -= CellUtil.estimatedHeapSizeOf(cell);
479 }
480 countdown--;
481 this.lastResult = rs;
482 }
483 }
484
485
486
487
488 if (null != values && values.length > 0 && callable.hasMoreResultsContext()) {
489
490
491 serverHasMoreResults = callable.getServerHasMoreResults();
492 }
493
494
495
496
497 } while (remainingResultSize > 0 && countdown > 0 && !serverHasMoreResults
498 && possiblyNextScanner(countdown, values == null));
499 }
500
501 @Override
502 public void close() {
503 if (!scanMetricsPublished) writeScanMetrics();
504 if (callable != null) {
505 callable.setClose();
506 try {
507 call(scan, callable, caller, scannerTimeout);
508 } catch (UnknownScannerException e) {
509
510
511
512 } catch (IOException e) {
513
514 LOG.warn("scanner failed to close. Exception follows: " + e);
515 }
516 callable = null;
517 }
518 closed = true;
519 }
520 }