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 private static final AtomicLong seekCount = new AtomicLong();
56
57 private ScanQueryMatcher matcher;
58
59
60
61
62
63 public StoreFileScanner(StoreFile.Reader reader, HFileScanner hfs, boolean useMVCC) {
64 this.reader = reader;
65 this.hfs = hfs;
66 this.enforceMVCC = useMVCC;
67 }
68
69
70
71
72
73 public static List<StoreFileScanner> getScannersForStoreFiles(
74 Collection<StoreFile> files,
75 boolean cacheBlocks,
76 boolean usePread) throws IOException {
77 return getScannersForStoreFiles(files, cacheBlocks,
78 usePread, false);
79 }
80
81
82
83
84 public static List<StoreFileScanner> getScannersForStoreFiles(
85 Collection<StoreFile> files, boolean cacheBlocks, boolean usePread,
86 boolean isCompaction) throws IOException {
87 return getScannersForStoreFiles(files, cacheBlocks, usePread, isCompaction,
88 null);
89 }
90
91
92
93
94
95
96 public static List<StoreFileScanner> getScannersForStoreFiles(
97 Collection<StoreFile> files, boolean cacheBlocks, boolean usePread,
98 boolean isCompaction, ScanQueryMatcher matcher) throws IOException {
99 List<StoreFileScanner> scanners = new ArrayList<StoreFileScanner>(
100 files.size());
101 for (StoreFile file : files) {
102 StoreFile.Reader r = file.createReader();
103 StoreFileScanner scanner = r.getStoreFileScanner(cacheBlocks, usePread,
104 isCompaction);
105 scanner.setScanQueryMatcher(matcher);
106 scanners.add(scanner);
107 }
108 return scanners;
109 }
110
111 public String toString() {
112 return "StoreFileScanner[" + hfs.toString() + ", cur=" + cur + "]";
113 }
114
115 public KeyValue peek() {
116 return cur;
117 }
118
119 public KeyValue next() throws IOException {
120 KeyValue retKey = cur;
121
122 try {
123
124 if (cur != null) {
125 hfs.next();
126 cur = hfs.getKeyValue();
127 skipKVsNewerThanReadpoint();
128 }
129 } catch(IOException e) {
130 throw new IOException("Could not iterate " + this, e);
131 }
132 return retKey;
133 }
134
135 public boolean seek(KeyValue key) throws IOException {
136 seekCount.incrementAndGet();
137
138 try {
139 try {
140 if(!seekAtOrAfter(hfs, key)) {
141 close();
142 return false;
143 }
144
145 cur = hfs.getKeyValue();
146
147 return skipKVsNewerThanReadpoint();
148 } finally {
149 realSeekDone = true;
150 }
151 } catch (IOException ioe) {
152 throw new IOException("Could not seek " + this + " to key " + key, ioe);
153 }
154 }
155
156 public boolean reseek(KeyValue key) throws IOException {
157 seekCount.incrementAndGet();
158
159 try {
160 try {
161 if (!reseekAtOrAfter(hfs, key)) {
162 close();
163 return false;
164 }
165 cur = hfs.getKeyValue();
166
167 return skipKVsNewerThanReadpoint();
168 } finally {
169 realSeekDone = true;
170 }
171 } catch (IOException ioe) {
172 throw new IOException("Could not reseek " + this + " to key " + key,
173 ioe);
174 }
175 }
176
177 protected boolean skipKVsNewerThanReadpoint() throws IOException {
178 long readPoint = MultiVersionConsistencyControl.getThreadReadPoint();
179
180
181
182 while(enforceMVCC
183 && cur != null
184 && (cur.getMemstoreTS() > readPoint)) {
185 hfs.next();
186 cur = hfs.getKeyValue();
187 }
188
189 if (cur == null) {
190 close();
191 return false;
192 }
193
194
195
196
197
198
199
200 if (cur.getMemstoreTS() <= readPoint) {
201 cur.setMemstoreTS(0);
202 }
203 return true;
204 }
205
206 public void close() {
207
208 cur = null;
209 }
210
211
212
213
214
215
216
217
218 public static boolean seekAtOrAfter(HFileScanner s, KeyValue k)
219 throws IOException {
220 int result = s.seekTo(k.getBuffer(), k.getKeyOffset(), k.getKeyLength());
221 if(result < 0) {
222
223 return s.seekTo();
224 } else if(result > 0) {
225
226
227 return s.next();
228 }
229
230 return true;
231 }
232
233 static boolean reseekAtOrAfter(HFileScanner s, KeyValue k)
234 throws IOException {
235
236 int result = s.reseekTo(k.getBuffer(), k.getKeyOffset(), k.getKeyLength());
237 if (result <= 0) {
238
239
240
241 if (!s.isSeeked()) {
242 return s.seekTo();
243 }
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) {
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 }