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.IOException;
23 import java.util.ArrayList;
24 import java.util.Collection;
25 import java.util.List;
26 import java.util.SortedSet;
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.classification.InterfaceAudience;
32 import org.apache.hadoop.hbase.HConstants;
33 import org.apache.hadoop.hbase.KeyValue;
34 import org.apache.hadoop.hbase.client.Scan;
35 import org.apache.hadoop.hbase.io.hfile.HFileScanner;
36 import org.apache.hadoop.hbase.regionserver.StoreFile.Reader;
37
38
39
40
41
42 @InterfaceAudience.LimitedPrivate("Coprocessor")
43 public class StoreFileScanner implements KeyValueScanner {
44 static final Log LOG = LogFactory.getLog(HStore.class);
45
46
47 private final StoreFile.Reader reader;
48 private final HFileScanner hfs;
49 private KeyValue cur = null;
50
51 private boolean realSeekDone;
52 private boolean delayedReseek;
53 private KeyValue delayedSeekKV;
54
55 private boolean enforceMVCC = false;
56
57 private static final AtomicLong seekCount = new AtomicLong();
58
59 private ScanQueryMatcher matcher;
60
61
62
63
64
65 public StoreFileScanner(StoreFile.Reader reader, HFileScanner hfs, boolean useMVCC) {
66 this.reader = reader;
67 this.hfs = hfs;
68 this.enforceMVCC = useMVCC;
69 }
70
71
72
73
74
75 public static List<StoreFileScanner> getScannersForStoreFiles(
76 Collection<StoreFile> files,
77 boolean cacheBlocks,
78 boolean usePread) throws IOException {
79 return getScannersForStoreFiles(files, cacheBlocks,
80 usePread, false);
81 }
82
83
84
85
86 public static List<StoreFileScanner> getScannersForStoreFiles(
87 Collection<StoreFile> files, boolean cacheBlocks, boolean usePread,
88 boolean isCompaction) throws IOException {
89 return getScannersForStoreFiles(files, cacheBlocks, usePread, isCompaction,
90 null);
91 }
92
93
94
95
96
97
98 public static List<StoreFileScanner> getScannersForStoreFiles(
99 Collection<StoreFile> files, boolean cacheBlocks, boolean usePread,
100 boolean isCompaction, ScanQueryMatcher matcher) throws IOException {
101 List<StoreFileScanner> scanners = new ArrayList<StoreFileScanner>(
102 files.size());
103 for (StoreFile file : files) {
104 StoreFile.Reader r = file.createReader();
105 StoreFileScanner scanner = r.getStoreFileScanner(cacheBlocks, usePread,
106 isCompaction);
107 scanner.setScanQueryMatcher(matcher);
108 scanners.add(scanner);
109 }
110 return scanners;
111 }
112
113 public String toString() {
114 return "StoreFileScanner[" + hfs.toString() + ", cur=" + cur + "]";
115 }
116
117 public KeyValue peek() {
118 return cur;
119 }
120
121 public KeyValue next() throws IOException {
122 KeyValue retKey = cur;
123
124 try {
125
126 if (cur != null) {
127 hfs.next();
128 cur = hfs.getKeyValue();
129 skipKVsNewerThanReadpoint();
130 }
131 } catch(IOException e) {
132 throw new IOException("Could not iterate " + this, e);
133 }
134 return retKey;
135 }
136
137 public boolean seek(KeyValue key) throws IOException {
138 seekCount.incrementAndGet();
139
140 try {
141 try {
142 if(!seekAtOrAfter(hfs, key)) {
143 close();
144 return false;
145 }
146
147 cur = hfs.getKeyValue();
148
149 return skipKVsNewerThanReadpoint();
150 } finally {
151 realSeekDone = true;
152 }
153 } catch (IOException ioe) {
154 throw new IOException("Could not seek " + this + " to key " + key, ioe);
155 }
156 }
157
158 public boolean reseek(KeyValue key) throws IOException {
159 seekCount.incrementAndGet();
160
161 try {
162 try {
163 if (!reseekAtOrAfter(hfs, key)) {
164 close();
165 return false;
166 }
167 cur = hfs.getKeyValue();
168
169 return skipKVsNewerThanReadpoint();
170 } finally {
171 realSeekDone = true;
172 }
173 } catch (IOException ioe) {
174 throw new IOException("Could not reseek " + this + " to key " + key,
175 ioe);
176 }
177 }
178
179 protected boolean skipKVsNewerThanReadpoint() throws IOException {
180 long readPoint = MultiVersionConsistencyControl.getThreadReadPoint();
181
182
183
184 while(enforceMVCC
185 && cur != null
186 && (cur.getMvccVersion() > readPoint)) {
187 hfs.next();
188 cur = hfs.getKeyValue();
189 }
190
191 if (cur == null) {
192 close();
193 return false;
194 }
195
196
197
198
199
200
201
202 if (cur.getMvccVersion() <= readPoint) {
203 cur.setMvccVersion(0);
204 }
205 return true;
206 }
207
208 public void close() {
209
210 cur = null;
211 }
212
213
214
215
216
217
218
219
220 public static boolean seekAtOrAfter(HFileScanner s, KeyValue k)
221 throws IOException {
222 int result = s.seekTo(k.getBuffer(), k.getKeyOffset(), k.getKeyLength());
223 if(result < 0) {
224 if (result == HConstants.INDEX_KEY_MAGIC) {
225
226 return true;
227 }
228
229 return s.seekTo();
230 } else if(result > 0) {
231
232
233 return s.next();
234 }
235
236 return true;
237 }
238
239 static boolean reseekAtOrAfter(HFileScanner s, KeyValue k)
240 throws IOException {
241
242 int result = s.reseekTo(k.getBuffer(), k.getKeyOffset(), k.getKeyLength());
243 if (result <= 0) {
244 if (result == HConstants.INDEX_KEY_MAGIC) {
245
246 return true;
247 }
248
249
250
251 if (!s.isSeeked()) {
252 return s.seekTo();
253 }
254 return true;
255 }
256
257
258 return s.next();
259 }
260
261 @Override
262 public long getSequenceID() {
263 return reader.getSequenceID();
264 }
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280 @Override
281 public boolean requestSeek(KeyValue kv, boolean forward, boolean useBloom)
282 throws IOException {
283 if (kv.getFamilyLength() == 0) {
284 useBloom = false;
285 }
286
287 boolean haveToSeek = true;
288 if (useBloom) {
289
290 if (reader.getBloomFilterType() == BloomType.ROWCOL) {
291 haveToSeek = reader.passesGeneralBloomFilter(kv.getBuffer(),
292 kv.getRowOffset(), kv.getRowLength(), kv.getBuffer(),
293 kv.getQualifierOffset(), kv.getQualifierLength());
294 } else if (this.matcher != null && !matcher.hasNullColumnInQuery() &&
295 (kv.isDeleteFamily() || kv.isDeleteFamilyVersion())) {
296
297
298 haveToSeek = reader.passesDeleteFamilyBloomFilter(kv.getBuffer(),
299 kv.getRowOffset(), kv.getRowLength());
300 }
301 }
302
303 delayedReseek = forward;
304 delayedSeekKV = kv;
305
306 if (haveToSeek) {
307
308
309 realSeekDone = false;
310 long maxTimestampInFile = reader.getMaxTimestamp();
311 long seekTimestamp = kv.getTimestamp();
312 if (seekTimestamp > maxTimestampInFile) {
313
314
315
316
317
318
319 cur = kv.createFirstOnRowColTS(maxTimestampInFile);
320 } else {
321
322
323
324
325 enforceSeek();
326 }
327 return cur != null;
328 }
329
330
331
332
333
334
335
336
337 cur = kv.createLastOnRowCol();
338
339 realSeekDone = true;
340 return true;
341 }
342
343 Reader getReaderForTesting() {
344 return reader;
345 }
346
347 @Override
348 public boolean realSeekDone() {
349 return realSeekDone;
350 }
351
352 @Override
353 public void enforceSeek() throws IOException {
354 if (realSeekDone)
355 return;
356
357 if (delayedReseek) {
358 reseek(delayedSeekKV);
359 } else {
360 seek(delayedSeekKV);
361 }
362 }
363
364 public void setScanQueryMatcher(ScanQueryMatcher matcher) {
365 this.matcher = matcher;
366 }
367
368 @Override
369 public boolean isFileScanner() {
370 return true;
371 }
372
373
374
375 static final long getSeekCount() {
376 return seekCount.get();
377 }
378
379 @Override
380 public boolean shouldUseScanner(Scan scan, SortedSet<byte[]> columns,
381 long oldestUnexpiredTS) {
382 return reader.passesTimerangeFilter(scan, oldestUnexpiredTS) &&
383 reader.passesBloomFilter(scan, columns);
384 }
385 }