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 static Result[] call(Scan scan, ScannerCallableWithReplicas callable,
280 RpcRetryingCaller<Result[]> caller, int scannerTimeout)
281 throws IOException, RuntimeException {
282 if (Thread.interrupted()) {
283 throw new InterruptedIOException();
284 }
285
286
287 return caller.callWithoutRetries(callable, scannerTimeout);
288 }
289
290 @InterfaceAudience.Private
291 protected ScannerCallableWithReplicas getScannerCallable(byte [] localStartKey,
292 int nbRows) {
293 scan.setStartRow(localStartKey);
294 ScannerCallable s =
295 new ScannerCallable(getConnection(), getTable(), scan, this.scanMetrics,
296 this.rpcControllerFactory);
297 s.setCaching(nbRows);
298 ScannerCallableWithReplicas sr = new ScannerCallableWithReplicas(tableName, getConnection(),
299 s, pool, primaryOperationTimeout, scan,
300 retries, scannerTimeout, caching, conf, caller);
301 return sr;
302 }
303
304
305
306
307
308
309
310
311
312
313
314
315 protected void writeScanMetrics() {
316 if (this.scanMetrics == null || scanMetricsPublished) {
317 return;
318 }
319 MapReduceProtos.ScanMetrics pScanMetrics = ProtobufUtil.toScanMetrics(scanMetrics);
320 scan.setAttribute(Scan.SCAN_ATTRIBUTES_METRICS_DATA, pScanMetrics.toByteArray());
321 scanMetricsPublished = true;
322 }
323
324 @Override
325 public Result next() throws IOException {
326
327 if (cache.size() == 0 && this.closed) {
328 return null;
329 }
330 if (cache.size() == 0) {
331 Result [] values = null;
332 long remainingResultSize = maxScannerResultSize;
333 int countdown = this.caching;
334
335
336 callable.setCaching(this.caching);
337
338
339 boolean skipFirst = false;
340 boolean retryAfterOutOfOrderException = true;
341 do {
342 try {
343 if (skipFirst) {
344
345
346 callable.setCaching(1);
347 values = call(scan, callable, caller, scannerTimeout);
348
349
350
351
352
353
354 if (values == null && callable.switchedToADifferentReplica()) {
355 if (this.lastResult != null) {
356 skipFirst = true;
357 }
358 this.currentRegion = callable.getHRegionInfo();
359 continue;
360 }
361 callable.setCaching(this.caching);
362 skipFirst = false;
363 }
364
365
366
367 values = call(scan, callable, caller, scannerTimeout);
368 if (skipFirst && values != null && values.length == 1) {
369 skipFirst = false;
370 values = call(scan, callable, caller, scannerTimeout);
371 }
372
373
374
375
376
377
378 if (values == null && callable.switchedToADifferentReplica()) {
379 if (this.lastResult != null) {
380 skipFirst = true;
381 }
382 this.currentRegion = callable.getHRegionInfo();
383 continue;
384 }
385 retryAfterOutOfOrderException = true;
386 } catch (DoNotRetryIOException e) {
387
388
389 if (e instanceof UnknownScannerException) {
390 long timeout = lastNext + scannerTimeout;
391
392
393
394 if (timeout < System.currentTimeMillis()) {
395 long elapsed = System.currentTimeMillis() - lastNext;
396 ScannerTimeoutException ex = new ScannerTimeoutException(
397 elapsed + "ms passed since the last invocation, " +
398 "timeout is currently set to " + scannerTimeout);
399 ex.initCause(e);
400 throw ex;
401 }
402 } else {
403
404
405 Throwable cause = e.getCause();
406 if ((cause != null && cause instanceof NotServingRegionException) ||
407 (cause != null && cause instanceof RegionServerStoppedException) ||
408 e instanceof OutOfOrderScannerNextException) {
409
410
411
412 } else {
413 throw e;
414 }
415 }
416
417 if (this.lastResult != null) {
418
419
420
421
422
423
424 this.scan.setStartRow(this.lastResult.getRow());
425
426
427
428 skipFirst = true;
429 }
430 if (e instanceof OutOfOrderScannerNextException) {
431 if (retryAfterOutOfOrderException) {
432 retryAfterOutOfOrderException = false;
433 } else {
434
435 throw new DoNotRetryIOException("Failed after retry of " +
436 "OutOfOrderScannerNextException: was there a rpc timeout?", e);
437 }
438 }
439
440 this.currentRegion = null;
441
442
443 callable = null;
444
445 continue;
446 }
447 long currentTime = System.currentTimeMillis();
448 if (this.scanMetrics != null ) {
449 this.scanMetrics.sumOfMillisSecBetweenNexts.addAndGet(currentTime-lastNext);
450 }
451 lastNext = currentTime;
452 if (values != null && values.length > 0) {
453 for (Result rs : values) {
454 cache.add(rs);
455
456 for (Cell cell : rs.rawCells()) {
457 remainingResultSize -= CellUtil.estimatedHeapSizeOf(cell);
458 }
459 countdown--;
460 this.lastResult = rs;
461 }
462 }
463
464 } while (remainingResultSize > 0 && countdown > 0 &&
465 possiblyNextScanner(countdown, values == null));
466 }
467
468 if (cache.size() > 0) {
469 return cache.poll();
470 }
471
472
473 writeScanMetrics();
474 return null;
475 }
476
477 @Override
478 public void close() {
479 if (!scanMetricsPublished) writeScanMetrics();
480 if (callable != null) {
481 callable.setClose();
482 try {
483 call(scan, callable, caller, scannerTimeout);
484 } catch (UnknownScannerException e) {
485
486
487
488 } catch (IOException e) {
489
490 LOG.warn("scanner failed to close. Exception follows: " + e);
491 }
492 callable = null;
493 }
494 closed = true;
495 }
496 }