1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.hadoop.hbase.regionserver;
21
22 import java.io.FileNotFoundException;
23 import java.io.IOException;
24 import java.util.ArrayList;
25 import java.util.Collection;
26 import java.util.List;
27 import java.util.concurrent.atomic.AtomicLong;
28
29 import org.apache.commons.logging.Log;
30 import org.apache.commons.logging.LogFactory;
31 import org.apache.hadoop.hbase.classification.InterfaceAudience;
32 import org.apache.hadoop.hbase.Cell;
33 import org.apache.hadoop.hbase.CellUtil;
34 import org.apache.hadoop.hbase.HConstants;
35 import org.apache.hadoop.hbase.KeyValue;
36 import org.apache.hadoop.hbase.KeyValueUtil;
37 import org.apache.hadoop.hbase.client.Scan;
38 import org.apache.hadoop.hbase.io.TimeRange;
39 import org.apache.hadoop.hbase.io.hfile.HFileScanner;
40 import org.apache.hadoop.hbase.regionserver.StoreFile.Reader;
41
42
43
44
45
46 @InterfaceAudience.LimitedPrivate("Coprocessor")
47 public class StoreFileScanner implements KeyValueScanner {
48 private static final Log LOG = LogFactory.getLog(HStore.class);
49
50
51 private final StoreFile.Reader reader;
52 private final HFileScanner hfs;
53 private Cell cur = null;
54 private boolean closed = false;
55
56 private boolean realSeekDone;
57 private boolean delayedReseek;
58 private Cell delayedSeekKV;
59
60 private boolean enforceMVCC = false;
61 private boolean hasMVCCInfo = false;
62
63
64 private boolean stopSkippingKVsIfNextRow = false;
65
66 private static AtomicLong seekCount;
67
68 private ScanQueryMatcher matcher;
69
70 private long readPt;
71
72
73
74
75
76 public StoreFileScanner(StoreFile.Reader reader, HFileScanner hfs, boolean useMVCC,
77 boolean hasMVCC, long readPt) {
78 this.readPt = readPt;
79 this.reader = reader;
80 this.hfs = hfs;
81 this.enforceMVCC = useMVCC;
82 this.hasMVCCInfo = hasMVCC;
83 }
84
85 boolean isPrimaryReplica() {
86 return reader.isPrimaryReplicaReader();
87 }
88
89
90
91
92
93 public static List<StoreFileScanner> getScannersForStoreFiles(
94 Collection<StoreFile> files,
95 boolean cacheBlocks,
96 boolean usePread, long readPt) throws IOException {
97 return getScannersForStoreFiles(files, cacheBlocks,
98 usePread, false, false, readPt);
99 }
100
101
102
103
104 public static List<StoreFileScanner> getScannersForStoreFiles(
105 Collection<StoreFile> files, boolean cacheBlocks, boolean usePread,
106 boolean isCompaction, boolean useDropBehind, long readPt) throws IOException {
107 return getScannersForStoreFiles(files, cacheBlocks, usePread, isCompaction,
108 useDropBehind, null, readPt);
109 }
110
111
112
113
114
115
116 public static List<StoreFileScanner> getScannersForStoreFiles(
117 Collection<StoreFile> files, boolean cacheBlocks, boolean usePread,
118 boolean isCompaction, boolean canUseDrop,
119 ScanQueryMatcher matcher, long readPt, boolean isPrimaryReplica) throws IOException {
120 List<StoreFileScanner> scanners = new ArrayList<StoreFileScanner>(
121 files.size());
122 for (StoreFile file : files) {
123 StoreFile.Reader r = file.createReader(canUseDrop);
124 r.setReplicaStoreFile(isPrimaryReplica);
125 StoreFileScanner scanner = r.getStoreFileScanner(cacheBlocks, usePread,
126 isCompaction, readPt);
127 scanner.setScanQueryMatcher(matcher);
128 scanners.add(scanner);
129 }
130 return scanners;
131 }
132
133 public static List<StoreFileScanner> getScannersForStoreFiles(
134 Collection<StoreFile> files, boolean cacheBlocks, boolean usePread,
135 boolean isCompaction, boolean canUseDrop,
136 ScanQueryMatcher matcher, long readPt) throws IOException {
137 return getScannersForStoreFiles(files, cacheBlocks, usePread, isCompaction, canUseDrop,
138 matcher, readPt, true);
139 }
140
141 public String toString() {
142 return "StoreFileScanner[" + hfs.toString() + ", cur=" + cur + "]";
143 }
144
145 public Cell peek() {
146 return cur;
147 }
148
149 public Cell next() throws IOException {
150 Cell retKey = cur;
151
152 try {
153
154 if (cur != null) {
155 hfs.next();
156 setCurrentCell(hfs.getKeyValue());
157 if (hasMVCCInfo || this.reader.isBulkLoaded()) {
158 skipKVsNewerThanReadpoint();
159 }
160 }
161 } catch (FileNotFoundException e) {
162 throw e;
163 } catch(IOException e) {
164 throw new IOException("Could not iterate " + this, e);
165 }
166 return retKey;
167 }
168
169 public boolean seek(Cell key) throws IOException {
170 if (seekCount != null) seekCount.incrementAndGet();
171
172 try {
173 try {
174 if(!seekAtOrAfter(hfs, key)) {
175 this.cur = null;
176 return false;
177 }
178
179 setCurrentCell(hfs.getKeyValue());
180
181 if (!hasMVCCInfo && this.reader.isBulkLoaded()) {
182 return skipKVsNewerThanReadpoint();
183 } else {
184 return !hasMVCCInfo ? true : skipKVsNewerThanReadpoint();
185 }
186 } finally {
187 realSeekDone = true;
188 }
189 } catch (FileNotFoundException e) {
190 throw e;
191 } catch (IOException ioe) {
192 throw new IOException("Could not seek " + this + " to key " + key, ioe);
193 }
194 }
195
196 public boolean reseek(Cell key) throws IOException {
197 if (seekCount != null) seekCount.incrementAndGet();
198
199 try {
200 try {
201 if (!reseekAtOrAfter(hfs, key)) {
202 this.cur = null;
203 return false;
204 }
205 setCurrentCell(hfs.getKeyValue());
206
207 if (!hasMVCCInfo && this.reader.isBulkLoaded()) {
208 return skipKVsNewerThanReadpoint();
209 } else {
210 return !hasMVCCInfo ? true : skipKVsNewerThanReadpoint();
211 }
212 } finally {
213 realSeekDone = true;
214 }
215 } catch (FileNotFoundException e) {
216 throw e;
217 } catch (IOException ioe) {
218 throw new IOException("Could not reseek " + this + " to key " + key,
219 ioe);
220 }
221 }
222
223 protected void setCurrentCell(Cell newVal) throws IOException {
224 this.cur = newVal;
225 if (this.cur != null && this.reader.isBulkLoaded()) {
226 CellUtil.setSequenceId(cur, this.reader.getSequenceID());
227 }
228 }
229
230 protected boolean skipKVsNewerThanReadpoint() throws IOException {
231
232
233 Cell startKV = cur;
234 while(enforceMVCC
235 && cur != null
236 && (cur.getMvccVersion() > readPt)) {
237 boolean hasNext = hfs.next();
238 setCurrentCell(hfs.getKeyValue());
239 if (hasNext && this.stopSkippingKVsIfNextRow
240 && getComparator().compareRows(cur.getRowArray(), cur.getRowOffset(),
241 cur.getRowLength(), startKV.getRowArray(), startKV.getRowOffset(),
242 startKV.getRowLength()) > 0) {
243 return false;
244 }
245 }
246
247 if (cur == null) {
248 return false;
249 }
250
251 return true;
252 }
253
254 public void close() {
255 cur = null;
256 if (closed) return;
257 if (this.reader != null) {
258 this.reader.decrementRefCount();
259 }
260 closed = true;
261 }
262
263
264
265
266
267
268
269
270 public static boolean seekAtOrAfter(HFileScanner s, Cell k)
271 throws IOException {
272 int result = s.seekTo(k);
273 if(result < 0) {
274 if (result == HConstants.INDEX_KEY_MAGIC) {
275
276 return true;
277 }
278
279 return s.seekTo();
280 } else if(result > 0) {
281
282
283 return s.next();
284 }
285
286 return true;
287 }
288
289 static boolean reseekAtOrAfter(HFileScanner s, Cell k)
290 throws IOException {
291
292 int result = s.reseekTo(k);
293 if (result <= 0) {
294 if (result == HConstants.INDEX_KEY_MAGIC) {
295
296 return true;
297 }
298
299
300
301 if (!s.isSeeked()) {
302 return s.seekTo();
303 }
304 return true;
305 }
306
307
308 return s.next();
309 }
310
311 @Override
312 public long getSequenceID() {
313 return reader.getSequenceID();
314 }
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330 @Override
331 public boolean requestSeek(Cell kv, boolean forward, boolean useBloom)
332 throws IOException {
333 if (kv.getFamilyLength() == 0) {
334 useBloom = false;
335 }
336
337 boolean haveToSeek = true;
338 if (useBloom) {
339
340 if (reader.getBloomFilterType() == BloomType.ROWCOL) {
341 haveToSeek = reader.passesGeneralBloomFilter(kv.getRowArray(),
342 kv.getRowOffset(), kv.getRowLength(), kv.getQualifierArray(),
343 kv.getQualifierOffset(), kv.getQualifierLength());
344 } else if (this.matcher != null && !matcher.hasNullColumnInQuery() &&
345 ((CellUtil.isDeleteFamily(kv) || CellUtil.isDeleteFamilyVersion(kv)))) {
346
347
348 haveToSeek = reader.passesDeleteFamilyBloomFilter(kv.getRowArray(),
349 kv.getRowOffset(), kv.getRowLength());
350 }
351 }
352
353 delayedReseek = forward;
354 delayedSeekKV = kv;
355
356 if (haveToSeek) {
357
358
359 realSeekDone = false;
360 long maxTimestampInFile = reader.getMaxTimestamp();
361 long seekTimestamp = kv.getTimestamp();
362 if (seekTimestamp > maxTimestampInFile) {
363
364
365
366
367
368
369 setCurrentCell(KeyValueUtil.createFirstOnRowColTS(kv, maxTimestampInFile));
370 } else {
371
372
373
374
375 enforceSeek();
376 }
377 return cur != null;
378 }
379
380
381
382
383
384
385
386
387 setCurrentCell(KeyValueUtil.createLastOnRowCol(kv));
388
389 realSeekDone = true;
390 return true;
391 }
392
393 Reader getReader() {
394 return reader;
395 }
396
397 KeyValue.KVComparator getComparator() {
398 return reader.getComparator();
399 }
400
401 @Override
402 public boolean realSeekDone() {
403 return realSeekDone;
404 }
405
406 @Override
407 public void enforceSeek() throws IOException {
408 if (realSeekDone)
409 return;
410
411 if (delayedReseek) {
412 reseek(delayedSeekKV);
413 } else {
414 seek(delayedSeekKV);
415 }
416 }
417
418 public void setScanQueryMatcher(ScanQueryMatcher matcher) {
419 this.matcher = matcher;
420 }
421
422 @Override
423 public boolean isFileScanner() {
424 return true;
425 }
426
427
428
429 static final long getSeekCount() {
430 return seekCount.get();
431 }
432 static final void instrument() {
433 seekCount = new AtomicLong();
434 }
435
436 @Override
437 public boolean shouldUseScanner(Scan scan, Store store, long oldestUnexpiredTS) {
438
439 byte[] cf = store.getFamily().getName();
440 TimeRange timeRange = scan.getColumnFamilyTimeRange().get(cf);
441 if (timeRange == null) {
442 timeRange = scan.getTimeRange();
443 }
444 return reader.passesTimerangeFilter(timeRange, oldestUnexpiredTS) && reader
445 .passesKeyRangeFilter(scan) && reader.passesBloomFilter(scan, scan.getFamilyMap().get(cf));
446 }
447
448 @Override
449 @SuppressWarnings("deprecation")
450 public boolean seekToPreviousRow(Cell originalKey) throws IOException {
451 try {
452 try {
453 boolean keepSeeking = false;
454 Cell key = originalKey;
455 do {
456 KeyValue seekKey = KeyValueUtil.createFirstOnRow(key.getRowArray(), key.getRowOffset(),
457 key.getRowLength());
458 if (seekCount != null) seekCount.incrementAndGet();
459 if (!hfs.seekBefore(seekKey.getBuffer(), seekKey.getKeyOffset(),
460 seekKey.getKeyLength())) {
461 this.cur = null;
462 return false;
463 }
464 KeyValue firstKeyOfPreviousRow = KeyValueUtil.createFirstOnRow(hfs.getKeyValue()
465 .getRowArray(), hfs.getKeyValue().getRowOffset(), hfs.getKeyValue().getRowLength());
466
467 if (seekCount != null) seekCount.incrementAndGet();
468 if (!seekAtOrAfter(hfs, firstKeyOfPreviousRow)) {
469 close();
470 return false;
471 }
472
473 setCurrentCell(hfs.getKeyValue());
474 this.stopSkippingKVsIfNextRow = true;
475 boolean resultOfSkipKVs;
476 try {
477 resultOfSkipKVs = skipKVsNewerThanReadpoint();
478 } finally {
479 this.stopSkippingKVsIfNextRow = false;
480 }
481 if (!resultOfSkipKVs
482 || getComparator().compareRows(cur, firstKeyOfPreviousRow) > 0) {
483 keepSeeking = true;
484 key = firstKeyOfPreviousRow;
485 continue;
486 } else {
487 keepSeeking = false;
488 }
489 } while (keepSeeking);
490 return true;
491 } finally {
492 realSeekDone = true;
493 }
494 } catch (FileNotFoundException e) {
495 throw e;
496 } catch (IOException ioe) {
497 throw new IOException("Could not seekToPreviousRow " + this + " to key "
498 + originalKey, ioe);
499 }
500 }
501
502 @Override
503 public boolean seekToLastRow() throws IOException {
504 byte[] lastRow = reader.getLastRowKey();
505 if (lastRow == null) {
506 return false;
507 }
508 KeyValue seekKey = KeyValueUtil.createFirstOnRow(lastRow);
509 if (seek(seekKey)) {
510 return true;
511 } else {
512 return seekToPreviousRow(seekKey);
513 }
514 }
515
516 @Override
517 public boolean backwardSeek(Cell key) throws IOException {
518 seek(key);
519 if (cur == null
520 || getComparator().compareRows(cur.getRowArray(), cur.getRowOffset(),
521 cur.getRowLength(), key.getRowArray(), key.getRowOffset(),
522 key.getRowLength()) > 0) {
523 return seekToPreviousRow(key);
524 }
525 return true;
526 }
527
528 @Override
529 public Cell getNextIndexedKey() {
530 return hfs.getNextIndexedKey();
531 }
532 }