View Javadoc

1   /**
2    * Copyright 2010 The Apache Software Foundation
3    *
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *     http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing, software
15   * distributed under the License is distributed on an "AS IS" BASIS,
16   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17   * See the License for the specific language governing permissions and
18   * limitations under the License.
19   */
20  
21  package org.apache.hadoop.hbase.regionserver;
22  
23  import org.apache.commons.logging.Log;
24  import org.apache.commons.logging.LogFactory;
25  import org.apache.hadoop.hbase.KeyValue;
26  import org.apache.hadoop.hbase.client.Scan;
27  import org.apache.hadoop.hbase.io.hfile.HFileScanner;
28  import org.apache.hadoop.hbase.regionserver.StoreFile.Reader;
29  
30  import java.io.IOException;
31  import java.util.ArrayList;
32  import java.util.Collection;
33  import java.util.List;
34  import java.util.SortedSet;
35  
36  /**
37   * KeyValueScanner adaptor over the Reader.  It also provides hooks into
38   * bloom filter things.
39   */
40  class StoreFileScanner implements KeyValueScanner {
41    static final Log LOG = LogFactory.getLog(Store.class);
42  
43    // the reader it comes from:
44    private final StoreFile.Reader reader;
45    private final HFileScanner hfs;
46    private KeyValue cur = null;
47    private boolean enforceMVCC = false;
48  
49    /**
50     * Implements a {@link KeyValueScanner} on top of the specified {@link HFileScanner}
51     * @param hfs HFile scanner
52     */
53    public StoreFileScanner(StoreFile.Reader reader, HFileScanner hfs, boolean useMVCC) {
54      this.reader = reader;
55      this.hfs = hfs;
56      this.enforceMVCC = useMVCC;
57    }
58  
59    /**
60     * Return an array of scanners corresponding to the given
61     * set of store files.
62     */
63    public static List<StoreFileScanner> getScannersForStoreFiles(
64        Collection<StoreFile> filesToCompact,
65        boolean cacheBlocks,
66        boolean usePread) throws IOException {
67      return getScannersForStoreFiles(filesToCompact, cacheBlocks, usePread, false);
68    }
69  
70    /**
71     * Return an array of scanners corresponding to the given set of store files.
72     */
73    public static List<StoreFileScanner> getScannersForStoreFiles(
74        Collection<StoreFile> files, boolean cacheBlocks, boolean usePread,
75        boolean isCompaction) throws IOException {
76      List<StoreFileScanner> scanners = new ArrayList<StoreFileScanner>(
77          files.size());
78      for (StoreFile file : files) {
79        StoreFile.Reader r = file.createReader();
80        scanners.add(r.getStoreFileScanner(cacheBlocks, usePread, isCompaction));
81      }
82      return scanners;
83    }
84  
85    public String toString() {
86      return "StoreFileScanner[" + hfs.toString() + ", cur=" + cur + "]";
87    }
88  
89    public KeyValue peek() {
90      return cur;
91    }
92  
93    public KeyValue next() throws IOException {
94      KeyValue retKey = cur;
95  
96      try {
97        // only seek if we aren't at the end. cur == null implies 'end'.
98        if (cur != null) {
99          hfs.next();
100         cur = hfs.getKeyValue();
101         skipKVsNewerThanReadpoint();
102       }
103     } catch(IOException e) {
104       throw new IOException("Could not iterate " + this, e);
105     }
106     return retKey;
107   }
108 
109   public boolean seek(KeyValue key) throws IOException {
110     try {
111       if(!seekAtOrAfter(hfs, key)) {
112         close();
113         return false;
114       }
115       cur = hfs.getKeyValue();
116       return skipKVsNewerThanReadpoint();
117     } catch(IOException ioe) {
118       throw new IOException("Could not seek " + this, ioe);
119     }
120   }
121 
122   public boolean reseek(KeyValue key) throws IOException {
123     try {
124       if (!reseekAtOrAfter(hfs, key)) {
125         close();
126         return false;
127       }
128       cur = hfs.getKeyValue();
129       return skipKVsNewerThanReadpoint();
130     } catch (IOException ioe) {
131       throw new IOException("Could not seek " + this, ioe);
132     }
133   }
134 
135   protected boolean skipKVsNewerThanReadpoint() throws IOException {
136     long readPoint = MultiVersionConsistencyControl.getThreadReadPoint();
137 
138     // We want to ignore all key-values that are newer than our current
139     // readPoint
140     while(enforceMVCC
141         && cur != null
142         && (cur.getMemstoreTS() > readPoint)) {
143       hfs.next();
144       cur = hfs.getKeyValue();
145     }
146 
147     if (cur == null) {
148       close();
149       return false;
150     }
151 
152     // For the optimisation in HBASE-4346, we set the KV's memstoreTS to
153     // 0, if it is older than all the scanners' read points. It is possible
154     // that a newer KV's memstoreTS was reset to 0. But, there is an
155     // older KV which was not reset to 0 (because it was
156     // not old enough during flush). Make sure that we set it correctly now,
157     // so that the comparision order does not change.
158     if (cur.getMemstoreTS() <= readPoint) {
159       cur.setMemstoreTS(0);
160     }
161     return true;
162   }
163 
164   public void close() {
165     // Nothing to close on HFileScanner?
166     cur = null;
167   }
168 
169   /**
170    *
171    * @param s
172    * @param k
173    * @return
174    * @throws IOException
175    */
176   public static boolean seekAtOrAfter(HFileScanner s, KeyValue k)
177   throws IOException {
178     int result = s.seekTo(k.getBuffer(), k.getKeyOffset(), k.getKeyLength());
179     if(result < 0) {
180       // Passed KV is smaller than first KV in file, work from start of file
181       return s.seekTo();
182     } else if(result > 0) {
183       // Passed KV is larger than current KV in file, if there is a next
184       // it is the "after", if not then this scanner is done.
185       return s.next();
186     }
187     // Seeked to the exact key
188     return true;
189   }
190 
191   static boolean reseekAtOrAfter(HFileScanner s, KeyValue k)
192   throws IOException {
193     //This function is similar to seekAtOrAfter function
194     int result = s.reseekTo(k.getBuffer(), k.getKeyOffset(), k.getKeyLength());
195     if (result <= 0) {
196       return true;
197     } else {
198       // passed KV is larger than current KV in file, if there is a next
199       // it is after, if not then this scanner is done.
200       return s.next();
201     }
202   }
203 
204   // StoreFile filter hook.
205   public boolean shouldSeek(Scan scan, final SortedSet<byte[]> columns) {
206     return reader.shouldSeek(scan, columns);
207   }
208 
209   @Override
210   public long getSequenceID() {
211     return reader.getSequenceID();
212   }
213 
214   @Override
215   public boolean seekExactly(KeyValue kv, boolean forward)
216       throws IOException {
217     if (reader.getBloomFilterType() != StoreFile.BloomType.ROWCOL ||
218         kv.getRowLength() == 0 || kv.getQualifierLength() == 0) {
219       return forward ? reseek(kv) : seek(kv);
220     }
221 
222     boolean isInBloom = reader.passesBloomFilter(kv.getBuffer(),
223         kv.getRowOffset(), kv.getRowLength(), kv.getBuffer(),
224         kv.getQualifierOffset(), kv.getQualifierLength());
225     if (isInBloom) {
226       // This row/column might be in this store file. Do a normal seek.
227       return forward ? reseek(kv) : seek(kv);
228     }
229 
230     // Create a fake key/value, so that this scanner only bubbles up to the top
231     // of the KeyValueHeap in StoreScanner after we scanned this row/column in
232     // all other store files. The query matcher will then just skip this fake
233     // key/value and the store scanner will progress to the next column.
234     cur = kv.createLastOnRowCol();
235     return true;
236   }
237 
238   Reader getReaderForTesting() {
239     return reader;
240   }
241 }