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.hbase.codec.prefixtree;
20  
21  import java.nio.ByteBuffer;
22  
23  import org.apache.hadoop.classification.InterfaceAudience;
24  import org.apache.hadoop.hbase.KeyValue;
25  import org.apache.hadoop.hbase.KeyValueTool;
26  import org.apache.hadoop.hbase.io.encoding.DataBlockEncoder.EncodedSeeker;
27  import org.apache.hbase.Cell;
28  import org.apache.hbase.cell.CellScannerPosition;
29  import org.apache.hbase.cell.CellTool;
30  import org.apache.hbase.codec.prefixtree.decode.DecoderFactory;
31  import org.apache.hbase.codec.prefixtree.decode.PrefixTreeArraySearcher;
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 KeyValueTool.copyKeyToNewByteBuffer(ptSearcher.getCurrent());
73    }
74  
75  
76    @Override
77    public ByteBuffer getValueShallowCopy() {
78      return CellTool.getValueBufferShallowCopy(ptSearcher.getCurrent());
79    }
80  
81    /**
82     * currently must do deep copy into new array
83     */
84    @Override
85    public ByteBuffer getKeyValueBuffer() {
86      return KeyValueTool.copyToNewByteBuffer(ptSearcher.getCurrent());
87    }
88  
89    /**
90     * currently must do deep copy into new array
91     */
92    @Override
93    public KeyValue getKeyValue() {
94      return KeyValueTool.copyToNewKeyValue(ptSearcher.getCurrent());
95    }
96  
97    /**
98     * Currently unused.
99     * <p/>
100    * A nice, lightweight reference, though the underlying cell is transient.  This method may return
101    * the same reference to the backing PrefixTreeCell repeatedly, while other implementations may
102    * return a different reference for each Cell.
103    * <p/>
104    * The goal will be to transition the upper layers of HBase, like Filters and KeyValueHeap, to use
105    * this method instead of the getKeyValue() methods above.
106    */
107 //  @Override
108   public Cell getCurrent() {
109     return ptSearcher.getCurrent();
110   }
111 
112   @Override
113   public void rewind() {
114     ptSearcher.positionAtFirstCell();
115   }
116 
117   @Override
118   public boolean next() {
119     return ptSearcher.next();
120   }
121 
122 //  @Override
123   public boolean advance() {
124     return ptSearcher.next();
125   }
126 
127 
128   private static final boolean USE_POSITION_BEFORE = false;
129 
130   /**
131    * Seek forward only (should be called reseekToKeyInBlock?).
132    * <p/>
133    * If the exact key is found look at the seekBefore variable and:<br/>
134    * - if true: go to the previous key if it's true<br/>
135    * - if false: stay on the exact key
136    * <p/>
137    * If the exact key is not found, then go to the previous key *if possible*, but remember to leave
138    * the scanner in a valid state if possible.
139    * <p/>
140    * @param keyOnlyBytes KeyValue format of a Cell's key at which to position the seeker
141    * @param offset offset into the keyOnlyBytes array
142    * @param length number of bytes of the keyOnlyBytes array to use
143    * @param forceBeforeOnExactMatch if an exact match is found and seekBefore=true, back up one Cell
144    * @return 0 if the seeker is on the exact key<br/>
145    *         1 if the seeker is not on the key for any reason, including seekBefore being true
146    */
147   @Override
148   public int seekToKeyInBlock(byte[] keyOnlyBytes, int offset, int length,
149       boolean forceBeforeOnExactMatch) {
150     if (USE_POSITION_BEFORE) {
151       return seekToOrBeforeUsingPositionAtOrBefore(keyOnlyBytes, offset, length,
152         forceBeforeOnExactMatch);
153     }else{
154       return seekToOrBeforeUsingPositionAtOrAfter(keyOnlyBytes, offset, length,
155         forceBeforeOnExactMatch);
156     }
157   }
158 
159 
160 
161   /*
162    * Support both of these options since the underlying PrefixTree supports both.  Possibly
163    * expand the EncodedSeeker to utilize them both.
164    */
165 
166   protected int seekToOrBeforeUsingPositionAtOrBefore(byte[] keyOnlyBytes, int offset, int length,
167       boolean forceBeforeOnExactMatch){
168     // this does a deep copy of the key byte[] because the CellSearcher interface wants a Cell
169     KeyValue kv = KeyValue.createKeyValueFromKey(keyOnlyBytes, offset, length);
170 
171     CellScannerPosition position = ptSearcher.seekForwardToOrBefore(kv);
172 
173     if(CellScannerPosition.AT == position){
174       if (forceBeforeOnExactMatch) {
175         ptSearcher.previous();
176         return 1;
177       }
178       return 0;
179     }
180 
181     return 1;
182   }
183 
184 
185   protected int seekToOrBeforeUsingPositionAtOrAfter(byte[] keyOnlyBytes, int offset, int length,
186       boolean forceBeforeOnExactMatch){
187     // this does a deep copy of the key byte[] because the CellSearcher interface wants a Cell
188     KeyValue kv = KeyValue.createKeyValueFromKey(keyOnlyBytes, offset, length);
189 
190     //should probably switch this to use the seekForwardToOrBefore method
191     CellScannerPosition position = ptSearcher.seekForwardToOrAfter(kv);
192 
193     if(CellScannerPosition.AT == position){
194       if (forceBeforeOnExactMatch) {
195         ptSearcher.previous();
196         return 1;
197       }
198       return 0;
199 
200     }
201 
202     if(CellScannerPosition.AFTER == position){
203       if(!ptSearcher.isBeforeFirst()){
204         ptSearcher.previous();
205       }
206       return 1;
207     }
208 
209     if(position == CellScannerPosition.AFTER_LAST){
210       return 1;
211     }
212 
213     throw new RuntimeException("unexpected CellScannerPosition:"+position);
214   }
215 
216 }