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.util.ArrayList;
22 import java.util.LinkedList;
23
24 import org.apache.commons.logging.Log;
25 import org.apache.commons.logging.LogFactory;
26 import org.apache.hadoop.classification.InterfaceAudience;
27 import org.apache.hadoop.classification.InterfaceStability;
28 import org.apache.hadoop.conf.Configuration;
29 import org.apache.hadoop.hbase.TableName;
30 import org.apache.hadoop.hbase.DoNotRetryIOException;
31 import org.apache.hadoop.hbase.HBaseConfiguration;
32 import org.apache.hadoop.hbase.HConstants;
33 import org.apache.hadoop.hbase.HRegionInfo;
34 import org.apache.hadoop.hbase.KeyValue;
35 import org.apache.hadoop.hbase.NotServingRegionException;
36 import org.apache.hadoop.hbase.UnknownScannerException;
37 import org.apache.hadoop.hbase.client.metrics.ScanMetrics;
38 import org.apache.hadoop.hbase.exceptions.OutOfOrderScannerNextException;
39 import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
40 import org.apache.hadoop.hbase.protobuf.generated.MapReduceProtos;
41 import org.apache.hadoop.hbase.regionserver.RegionServerStoppedException;
42 import org.apache.hadoop.hbase.util.Bytes;
43
44
45
46
47
48
49 @InterfaceAudience.Public
50 @InterfaceStability.Stable
51 public class ClientScanner extends AbstractClientScanner {
52 private final Log LOG = LogFactory.getLog(this.getClass());
53 private Scan scan;
54 private boolean closed = false;
55
56
57 private HRegionInfo currentRegion = null;
58 private ScannerCallable callable = null;
59 private final LinkedList<Result> cache = new LinkedList<Result>();
60 private final int caching;
61 private long lastNext;
62
63 private Result lastResult = null;
64 private ScanMetrics scanMetrics = null;
65 private final long maxScannerResultSize;
66 private final HConnection connection;
67 private final TableName tableName;
68 private final int scannerTimeout;
69 private boolean scanMetricsPublished = false;
70 private RpcRetryingCaller<Result []> caller;
71
72
73
74
75
76
77
78
79
80
81
82 public ClientScanner(final Configuration conf, final Scan scan,
83 final TableName tableName) throws IOException {
84 this(conf, scan, tableName, HConnectionManager.getConnection(conf));
85 }
86
87
88
89
90
91
92
93
94
95
96
97
98 public ClientScanner(final Configuration conf, final Scan scan, final TableName tableName,
99 HConnection connection) throws IOException {
100 this(conf, scan, tableName, connection, new RpcRetryingCallerFactory(conf));
101 }
102
103
104
105
106
107
108
109
110
111
112 public ClientScanner(final Configuration conf, final Scan scan, final TableName tableName,
113 HConnection connection, RpcRetryingCallerFactory rpcFactory) throws IOException {
114 if (LOG.isTraceEnabled()) {
115 LOG.trace("Scan table=" + tableName
116 + ", startRow=" + Bytes.toStringBinary(scan.getStartRow()));
117 }
118 this.scan = scan;
119 this.tableName = tableName;
120 this.lastNext = System.currentTimeMillis();
121 this.connection = connection;
122 if (scan.getMaxResultSize() > 0) {
123 this.maxScannerResultSize = scan.getMaxResultSize();
124 } else {
125 this.maxScannerResultSize = conf.getLong(
126 HConstants.HBASE_CLIENT_SCANNER_MAX_RESULT_SIZE_KEY,
127 HConstants.DEFAULT_HBASE_CLIENT_SCANNER_MAX_RESULT_SIZE);
128 }
129 this.scannerTimeout = HBaseConfiguration.getInt(conf,
130 HConstants.HBASE_CLIENT_SCANNER_TIMEOUT_PERIOD,
131 HConstants.HBASE_REGIONSERVER_LEASE_PERIOD_KEY,
132 HConstants.DEFAULT_HBASE_CLIENT_SCANNER_TIMEOUT_PERIOD);
133
134
135 byte[] enableMetrics = scan.getAttribute(
136 Scan.SCAN_ATTRIBUTES_METRICS_ENABLE);
137 if (enableMetrics != null && Bytes.toBoolean(enableMetrics)) {
138 scanMetrics = new ScanMetrics();
139 }
140
141
142 if (this.scan.getCaching() > 0) {
143 this.caching = this.scan.getCaching();
144 } else {
145 this.caching = conf.getInt(
146 HConstants.HBASE_CLIENT_SCANNER_CACHING,
147 HConstants.DEFAULT_HBASE_CLIENT_SCANNER_CACHING);
148 }
149
150 this.caller = rpcFactory.<Result[]> newCaller();
151
152
153 nextScanner(this.caching, false);
154 }
155
156 protected HConnection getConnection() {
157 return this.connection;
158 }
159
160 protected TableName getTableName() {
161 return this.tableName;
162 }
163
164 protected Scan getScan() {
165 return scan;
166 }
167
168 protected long getTimestamp() {
169 return lastNext;
170 }
171
172
173 private boolean checkScanStopRow(final byte [] endKey) {
174 if (this.scan.getStopRow().length > 0) {
175
176 byte [] stopRow = scan.getStopRow();
177 int cmp = Bytes.compareTo(stopRow, 0, stopRow.length,
178 endKey, 0, endKey.length);
179 if (cmp <= 0) {
180
181
182 return true;
183 }
184 }
185 return false;
186 }
187
188
189
190
191
192
193
194
195
196
197 private boolean nextScanner(int nbRows, final boolean done)
198 throws IOException {
199
200 if (this.callable != null) {
201 this.callable.setClose();
202 this.caller.callWithRetries(callable);
203 this.callable = null;
204 }
205
206
207 byte [] localStartKey;
208
209
210 if (this.currentRegion != null) {
211 byte [] endKey = this.currentRegion.getEndKey();
212 if (endKey == null ||
213 Bytes.equals(endKey, HConstants.EMPTY_BYTE_ARRAY) ||
214 checkScanStopRow(endKey) ||
215 done) {
216 close();
217 if (LOG.isDebugEnabled()) {
218 LOG.debug("Finished region=" + this.currentRegion);
219 }
220 return false;
221 }
222 localStartKey = endKey;
223 if (LOG.isDebugEnabled()) {
224 LOG.debug("Finished with region " + this.currentRegion);
225 }
226 } else {
227 localStartKey = this.scan.getStartRow();
228 }
229
230 if (LOG.isDebugEnabled() && this.currentRegion != null) {
231
232 LOG.debug("Advancing internal scanner to startKey at '" +
233 Bytes.toStringBinary(localStartKey) + "'");
234 }
235 try {
236 callable = getScannerCallable(localStartKey, nbRows);
237
238
239 this.caller.callWithRetries(callable);
240 this.currentRegion = callable.getHRegionInfo();
241 if (this.scanMetrics != null) {
242 this.scanMetrics.countOfRegions.incrementAndGet();
243 }
244 } catch (IOException e) {
245 close();
246 throw e;
247 }
248 return true;
249 }
250
251 protected ScannerCallable getScannerCallable(byte [] localStartKey,
252 int nbRows) {
253 scan.setStartRow(localStartKey);
254 ScannerCallable s = new ScannerCallable(getConnection(),
255 getTableName(), scan, this.scanMetrics);
256 s.setCaching(nbRows);
257 return s;
258 }
259
260
261
262
263
264
265
266
267
268
269
270
271 private void writeScanMetrics() {
272 if (this.scanMetrics == null || scanMetricsPublished) {
273 return;
274 }
275 MapReduceProtos.ScanMetrics pScanMetrics = ProtobufUtil.toScanMetrics(scanMetrics);
276 scan.setAttribute(Scan.SCAN_ATTRIBUTES_METRICS_DATA, pScanMetrics.toByteArray());
277 scanMetricsPublished = true;
278 }
279
280 @Override
281 public Result next() throws IOException {
282
283 if (cache.size() == 0 && this.closed) {
284 return null;
285 }
286 if (cache.size() == 0) {
287 Result [] values = null;
288 long remainingResultSize = maxScannerResultSize;
289 int countdown = this.caching;
290
291
292 callable.setCaching(this.caching);
293
294
295 boolean skipFirst = false;
296 boolean retryAfterOutOfOrderException = true;
297 do {
298 try {
299 if (skipFirst) {
300
301
302 callable.setCaching(1);
303 values = this.caller.callWithRetries(callable);
304 callable.setCaching(this.caching);
305 skipFirst = false;
306 }
307
308
309
310 values = this.caller.callWithRetries(callable);
311 if (skipFirst && values != null && values.length == 1) {
312 skipFirst = false;
313 values = this.caller.callWithRetries(callable);
314 }
315 retryAfterOutOfOrderException = true;
316 } catch (DoNotRetryIOException e) {
317
318
319 if (e instanceof UnknownScannerException) {
320 long timeout = lastNext + scannerTimeout;
321
322
323
324 if (timeout < System.currentTimeMillis()) {
325 long elapsed = System.currentTimeMillis() - lastNext;
326 ScannerTimeoutException ex = new ScannerTimeoutException(
327 elapsed + "ms passed since the last invocation, " +
328 "timeout is currently set to " + scannerTimeout);
329 ex.initCause(e);
330 throw ex;
331 }
332 } else {
333
334
335 Throwable cause = e.getCause();
336 if ((cause != null && cause instanceof NotServingRegionException) ||
337 (cause != null && cause instanceof RegionServerStoppedException) ||
338 e instanceof OutOfOrderScannerNextException) {
339
340
341
342 } else {
343 throw e;
344 }
345 }
346
347 if (this.lastResult != null) {
348 this.scan.setStartRow(this.lastResult.getRow());
349
350
351 skipFirst = true;
352 }
353 if (e instanceof OutOfOrderScannerNextException) {
354 if (retryAfterOutOfOrderException) {
355 retryAfterOutOfOrderException = false;
356 } else {
357
358 throw new DoNotRetryIOException("Failed after retry of " +
359 "OutOfOrderScannerNextException: was there a rpc timeout?", e);
360 }
361 }
362
363 this.currentRegion = null;
364
365
366 callable = null;
367
368 continue;
369 }
370 long currentTime = System.currentTimeMillis();
371 if (this.scanMetrics != null ) {
372 this.scanMetrics.sumOfMillisSecBetweenNexts.addAndGet(currentTime-lastNext);
373 }
374 lastNext = currentTime;
375 if (values != null && values.length > 0) {
376 for (Result rs : values) {
377 cache.add(rs);
378 for (KeyValue kv : rs.raw()) {
379 remainingResultSize -= kv.heapSize();
380 }
381 countdown--;
382 this.lastResult = rs;
383 }
384 }
385
386 } while (remainingResultSize > 0 && countdown > 0 && nextScanner(countdown, values == null));
387 }
388
389 if (cache.size() > 0) {
390 return cache.poll();
391 }
392
393
394 writeScanMetrics();
395 return null;
396 }
397
398
399
400
401
402
403
404
405
406
407 @Override
408 public Result [] next(int nbRows) throws IOException {
409
410 ArrayList<Result> resultSets = new ArrayList<Result>(nbRows);
411 for(int i = 0; i < nbRows; i++) {
412 Result next = next();
413 if (next != null) {
414 resultSets.add(next);
415 } else {
416 break;
417 }
418 }
419 return resultSets.toArray(new Result[resultSets.size()]);
420 }
421
422 @Override
423 public void close() {
424 if (!scanMetricsPublished) writeScanMetrics();
425 if (callable != null) {
426 callable.setClose();
427 try {
428 this.caller.callWithRetries(callable);
429 } catch (IOException e) {
430
431
432
433
434 }
435 callable = null;
436 }
437 closed = true;
438 }
439 }