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