View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  
19  package org.apache.hadoop.hbase.codec.prefixtree;
20  
21  import java.nio.ByteBuffer;
22  
23  import org.apache.hadoop.classification.InterfaceAudience;
24  import org.apache.hadoop.hbase.Cell;
25  import org.apache.hadoop.hbase.CellUtil;
26  import org.apache.hadoop.hbase.KeyValue;
27  import org.apache.hadoop.hbase.KeyValueUtil;
28  import org.apache.hadoop.hbase.codec.prefixtree.decode.DecoderFactory;
29  import org.apache.hadoop.hbase.codec.prefixtree.decode.PrefixTreeArraySearcher;
30  import org.apache.hadoop.hbase.codec.prefixtree.scanner.CellScannerPosition;
31  import org.apache.hadoop.hbase.io.encoding.DataBlockEncoder.EncodedSeeker;
32  
33  /**
34   * These methods have the same definition as any implementation of the EncodedSeeker.
35   *
36   * In the future, the EncodedSeeker could be modified to work with the Cell interface directly.  It
37   * currently returns a new KeyValue object each time getKeyValue is called.  This is not horrible,
38   * but in order to create a new KeyValue object, we must first allocate a new byte[] and copy in
39   * the data from the PrefixTreeCell.  It is somewhat heavyweight right now.
40   */
41  @InterfaceAudience.Private
42  public class PrefixTreeSeeker implements EncodedSeeker {
43  
44    protected ByteBuffer block;
45    protected boolean includeMvccVersion;
46    protected PrefixTreeArraySearcher ptSearcher;
47  
48    public PrefixTreeSeeker(boolean includeMvccVersion) {
49      this.includeMvccVersion = includeMvccVersion;
50    }
51  
52    @Override
53    public void setCurrentBuffer(ByteBuffer fullBlockBuffer) {
54      block = fullBlockBuffer;
55      ptSearcher = DecoderFactory.checkOut(block, includeMvccVersion);
56      rewind();
57    }
58  
59    /**
60     * Currently unused.
61     * <p/>
62     * TODO performance leak. should reuse the searchers. hbase does not currently have a hook where
63     * this can be called
64     */
65    public void releaseCurrentSearcher(){
66      DecoderFactory.checkIn(ptSearcher);
67    }
68  
69  
70    @Override
71    public ByteBuffer getKeyDeepCopy() {
72      return KeyValueUtil.copyKeyToNewByteBuffer(ptSearcher.current());
73    }
74  
75  
76    @Override
77    public ByteBuffer getValueShallowCopy() {
78      return CellUtil.getValueBufferShallowCopy(ptSearcher.current());
79    }
80  
81    /**
82     * currently must do deep copy into new array
83     */
84    @Override
85    public ByteBuffer getKeyValueBuffer() {
86      return KeyValueUtil.copyToNewByteBuffer(ptSearcher.current());
87    }
88  
89    /**
90     * currently must do deep copy into new array
91     */
92    @Override
93    public KeyValue getKeyValue() {
94      if (ptSearcher.current() == null) {
95        return null;
96      }
97      return KeyValueUtil.copyToNewKeyValue(ptSearcher.current());
98    }
99  
100   /**
101    * Currently unused.
102    * <p/>
103    * A nice, lightweight reference, though the underlying cell is transient. This method may return
104    * the same reference to the backing PrefixTreeCell repeatedly, while other implementations may
105    * return a different reference for each Cell.
106    * <p/>
107    * The goal will be to transition the upper layers of HBase, like Filters and KeyValueHeap, to
108    * use this method instead of the getKeyValue() methods above.
109    */
110   public Cell get() {
111     return ptSearcher.current();
112   }
113 
114   @Override
115   public void rewind() {
116     ptSearcher.positionAtFirstCell();
117   }
118 
119   @Override
120   public boolean next() {
121     return ptSearcher.advance();
122   }
123 
124 //  @Override
125   public boolean advance() {
126     return ptSearcher.advance();
127   }
128 
129 
130   private static final boolean USE_POSITION_BEFORE = false;
131 
132   /**
133    * Seek forward only (should be called reseekToKeyInBlock?).
134    * <p/>
135    * If the exact key is found look at the seekBefore variable and:<br/>
136    * - if true: go to the previous key if it's true<br/>
137    * - if false: stay on the exact key
138    * <p/>
139    * If the exact key is not found, then go to the previous key *if possible*, but remember to
140    * leave the scanner in a valid state if possible.
141    * <p/>
142    * @param keyOnlyBytes KeyValue format of a Cell's key at which to position the seeker
143    * @param offset offset into the keyOnlyBytes array
144    * @param length number of bytes of the keyOnlyBytes array to use
145    * @param forceBeforeOnExactMatch if an exact match is found and seekBefore=true, back up 1 Cell
146    * @return 0 if the seeker is on the exact key<br/>
147    *         1 if the seeker is not on the key for any reason, including seekBefore being true
148    */
149   @Override
150   public int seekToKeyInBlock(byte[] keyOnlyBytes, int offset, int length,
151       boolean forceBeforeOnExactMatch) {
152     if (USE_POSITION_BEFORE) {
153       return seekToOrBeforeUsingPositionAtOrBefore(keyOnlyBytes, offset, length,
154         forceBeforeOnExactMatch);
155     }else{
156       return seekToOrBeforeUsingPositionAtOrAfter(keyOnlyBytes, offset, length,
157         forceBeforeOnExactMatch);
158     }
159   }
160 
161 
162 
163   /*
164    * Support both of these options since the underlying PrefixTree supports both.  Possibly
165    * expand the EncodedSeeker to utilize them both.
166    */
167 
168   protected int seekToOrBeforeUsingPositionAtOrBefore(byte[] keyOnlyBytes, int offset, int length,
169       boolean seekBefore){
170     // this does a deep copy of the key byte[] because the CellSearcher interface wants a Cell
171     KeyValue kv = KeyValue.createKeyValueFromKey(keyOnlyBytes, offset, length);
172 
173     CellScannerPosition position = ptSearcher.seekForwardToOrBefore(kv);
174 
175     if(CellScannerPosition.AT == position){
176       if (seekBefore) {
177         ptSearcher.previous();
178         return 1;
179       }
180       return 0;
181     }
182 
183     return 1;
184   }
185 
186 
187   protected int seekToOrBeforeUsingPositionAtOrAfter(byte[] keyOnlyBytes, int offset, int length,
188       boolean seekBefore){
189     // this does a deep copy of the key byte[] because the CellSearcher interface wants a Cell
190     KeyValue kv = KeyValue.createKeyValueFromKey(keyOnlyBytes, offset, length);
191 
192     //should probably switch this to use the seekForwardToOrBefore method
193     CellScannerPosition position = ptSearcher.seekForwardToOrAfter(kv);
194 
195     if(CellScannerPosition.AT == position){
196       if (seekBefore) {
197         ptSearcher.previous();
198         return 1;
199       }
200       return 0;
201 
202     }
203 
204     if(CellScannerPosition.AFTER == position){
205       if(!ptSearcher.isBeforeFirst()){
206         ptSearcher.previous();
207       }
208       return 1;
209     }
210 
211     if(position == CellScannerPosition.AFTER_LAST){
212       if (seekBefore) {
213         ptSearcher.previous();
214       }
215       return 1;
216     }
217 
218     throw new RuntimeException("unexpected CellScannerPosition:"+position);
219   }
220 
221 }