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