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