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 private boolean hasMVCCInfo = false;
55
56 private static AtomicLong seekCount;
57
58 private ScanQueryMatcher matcher;
59
60
61
62
63
64 public StoreFileScanner(StoreFile.Reader reader, HFileScanner hfs, boolean useMVCC, boolean hasMVCC) {
65 this.reader = reader;
66 this.hfs = hfs;
67 this.enforceMVCC = useMVCC;
68 this.hasMVCCInfo = hasMVCC;
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 if (hasMVCCInfo)
130 skipKVsNewerThanReadpoint();
131 }
132 } catch(IOException e) {
133 throw new IOException("Could not iterate " + this, e);
134 }
135 return retKey;
136 }
137
138 public boolean seek(KeyValue key) throws IOException {
139 if (seekCount != null) seekCount.incrementAndGet();
140
141 try {
142 try {
143 if(!seekAtOrAfter(hfs, key)) {
144 close();
145 return false;
146 }
147
148 cur = hfs.getKeyValue();
149
150 return !hasMVCCInfo ? true : skipKVsNewerThanReadpoint();
151 } finally {
152 realSeekDone = true;
153 }
154 } catch (IOException ioe) {
155 throw new IOException("Could not seek " + this + " to key " + key, ioe);
156 }
157 }
158
159 public boolean reseek(KeyValue key) throws IOException {
160 if (seekCount != null) seekCount.incrementAndGet();
161
162 try {
163 try {
164 if (!reseekAtOrAfter(hfs, key)) {
165 close();
166 return false;
167 }
168 cur = hfs.getKeyValue();
169
170 return !hasMVCCInfo ? true : skipKVsNewerThanReadpoint();
171 } finally {
172 realSeekDone = true;
173 }
174 } catch (IOException ioe) {
175 throw new IOException("Could not reseek " + this + " to key " + key,
176 ioe);
177 }
178 }
179
180 protected boolean skipKVsNewerThanReadpoint() throws IOException {
181 long readPoint = MultiVersionConsistencyControl.getThreadReadPoint();
182
183
184
185 while(enforceMVCC
186 && cur != null
187 && (cur.getMemstoreTS() > readPoint)) {
188 hfs.next();
189 cur = hfs.getKeyValue();
190 }
191
192 if (cur == null) {
193 close();
194 return false;
195 }
196
197
198
199
200
201
202
203 if (cur.getMemstoreTS() <= readPoint) {
204 cur.setMemstoreTS(0);
205 }
206 return true;
207 }
208
209 public void close() {
210
211 cur = null;
212 }
213
214
215
216
217
218
219
220
221 public static boolean seekAtOrAfter(HFileScanner s, KeyValue k)
222 throws IOException {
223 int result = s.seekTo(k.getBuffer(), k.getKeyOffset(), k.getKeyLength());
224 if(result < 0) {
225
226 return s.seekTo();
227 } else if(result > 0) {
228
229
230 return s.next();
231 }
232
233 return true;
234 }
235
236 static boolean reseekAtOrAfter(HFileScanner s, KeyValue k)
237 throws IOException {
238
239 int result = s.reseekTo(k.getBuffer(), k.getKeyOffset(), k.getKeyLength());
240 if (result <= 0) {
241
242
243
244 if (!s.isSeeked()) {
245 return s.seekTo();
246 }
247 return true;
248 } else {
249
250
251 return s.next();
252 }
253 }
254
255 @Override
256 public long getSequenceID() {
257 return reader.getSequenceID();
258 }
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274 @Override
275 public boolean requestSeek(KeyValue kv, boolean forward, boolean useBloom)
276 throws IOException {
277 if (kv.getFamilyLength() == 0) {
278 useBloom = false;
279 }
280
281 boolean haveToSeek = true;
282 if (useBloom) {
283
284 if (reader.getBloomFilterType() == StoreFile.BloomType.ROWCOL) {
285 haveToSeek = reader.passesGeneralBloomFilter(kv.getBuffer(),
286 kv.getRowOffset(), kv.getRowLength(), kv.getBuffer(),
287 kv.getQualifierOffset(), kv.getQualifierLength());
288 } else if (this.matcher != null && !matcher.hasNullColumnInQuery() &&
289 kv.isDeleteFamily()) {
290
291
292 haveToSeek = reader.passesDeleteFamilyBloomFilter(kv.getBuffer(),
293 kv.getRowOffset(), kv.getRowLength());
294 }
295 }
296
297 delayedReseek = forward;
298 delayedSeekKV = kv;
299
300 if (haveToSeek) {
301
302
303 realSeekDone = false;
304 long maxTimestampInFile = reader.getMaxTimestamp();
305 long seekTimestamp = kv.getTimestamp();
306 if (seekTimestamp > maxTimestampInFile) {
307
308
309
310
311
312
313 cur = kv.createFirstOnRowColTS(maxTimestampInFile);
314 } else {
315
316
317
318
319 enforceSeek();
320 }
321 return cur != null;
322 }
323
324
325
326
327
328
329
330
331 cur = kv.createLastOnRowCol();
332
333 realSeekDone = true;
334 return true;
335 }
336
337 Reader getReaderForTesting() {
338 return reader;
339 }
340
341 @Override
342 public boolean realSeekDone() {
343 return realSeekDone;
344 }
345
346 @Override
347 public void enforceSeek() throws IOException {
348 if (realSeekDone)
349 return;
350
351 if (delayedReseek) {
352 reseek(delayedSeekKV);
353 } else {
354 seek(delayedSeekKV);
355 }
356 }
357
358 public void setScanQueryMatcher(ScanQueryMatcher matcher) {
359 this.matcher = matcher;
360 }
361
362 @Override
363 public boolean isFileScanner() {
364 return true;
365 }
366
367
368
369 static final long getSeekCount() {
370 return seekCount.get();
371 }
372 static final void instrument() {
373 seekCount = new AtomicLong();
374 }
375
376 @Override
377 public boolean shouldUseScanner(Scan scan, SortedSet<byte[]> columns, long oldestUnexpiredTS) {
378 return reader.passesTimerangeFilter(scan, oldestUnexpiredTS)
379 && reader.passesKeyRangeFilter(scan) && reader.passesBloomFilter(scan, columns);
380 }
381 }