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      return KeyValueUtil.copyToNewKeyValue(ptSearcher.current());
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
105    * use this method instead of the getKeyValue() methods above.
106    */
107   public Cell get() {
108     return ptSearcher.current();
109   }
110 
111   @Override
112   public void rewind() {
113     ptSearcher.positionAtFirstCell();
114   }
115 
116   @Override
117   public boolean next() {
118     return ptSearcher.advance();
119   }
120 
121 //  @Override
122   public boolean advance() {
123     return ptSearcher.advance();
124   }
125 
126 
127   private static final boolean USE_POSITION_BEFORE = false;
128 
129   /**
130    * Seek forward only (should be called reseekToKeyInBlock?).
131    * <p/>
132    * If the exact key is found look at the seekBefore variable and:<br/>
133    * - if true: go to the previous key if it's true<br/>
134    * - if false: stay on the exact key
135    * <p/>
136    * If the exact key is not found, then go to the previous key *if possible*, but remember to
137    * leave the scanner in a valid state if possible.
138    * <p/>
139    * @param keyOnlyBytes KeyValue format of a Cell's key at which to position the seeker
140    * @param offset offset into the keyOnlyBytes array
141    * @param length number of bytes of the keyOnlyBytes array to use
142    * @param forceBeforeOnExactMatch if an exact match is found and seekBefore=true, back up 1 Cell
143    * @return 0 if the seeker is on the exact key<br/>
144    *         1 if the seeker is not on the key for any reason, including seekBefore being true
145    */
146   @Override
147   public int seekToKeyInBlock(byte[] keyOnlyBytes, int offset, int length,
148       boolean forceBeforeOnExactMatch) {
149     if (USE_POSITION_BEFORE) {
150       return seekToOrBeforeUsingPositionAtOrBefore(keyOnlyBytes, offset, length,
151         forceBeforeOnExactMatch);
152     }else{
153       return seekToOrBeforeUsingPositionAtOrAfter(keyOnlyBytes, offset, length,
154         forceBeforeOnExactMatch);
155     }
156   }
157 
158 
159 
160   /*
161    * Support both of these options since the underlying PrefixTree supports both.  Possibly
162    * expand the EncodedSeeker to utilize them both.
163    */
164 
165   protected int seekToOrBeforeUsingPositionAtOrBefore(byte[] keyOnlyBytes, int offset, int length,
166       boolean forceBeforeOnExactMatch){
167     // this does a deep copy of the key byte[] because the CellSearcher interface wants a Cell
168     KeyValue kv = KeyValue.createKeyValueFromKey(keyOnlyBytes, offset, length);
169 
170     CellScannerPosition position = ptSearcher.seekForwardToOrBefore(kv);
171 
172     if(CellScannerPosition.AT == position){
173       if (forceBeforeOnExactMatch) {
174         ptSearcher.previous();
175         return 1;
176       }
177       return 0;
178     }
179 
180     return 1;
181   }
182 
183 
184   protected int seekToOrBeforeUsingPositionAtOrAfter(byte[] keyOnlyBytes, int offset, int length,
185       boolean forceBeforeOnExactMatch){
186     // this does a deep copy of the key byte[] because the CellSearcher interface wants a Cell
187     KeyValue kv = KeyValue.createKeyValueFromKey(keyOnlyBytes, offset, length);
188 
189     //should probably switch this to use the seekForwardToOrBefore method
190     CellScannerPosition position = ptSearcher.seekForwardToOrAfter(kv);
191 
192     if(CellScannerPosition.AT == position){
193       if (forceBeforeOnExactMatch) {
194         ptSearcher.previous();
195         return 1;
196       }
197       return 0;
198 
199     }
200 
201     if(CellScannerPosition.AFTER == position){
202       if(!ptSearcher.isBeforeFirst()){
203         ptSearcher.previous();
204       }
205       return 1;
206     }
207 
208     if(position == CellScannerPosition.AFTER_LAST){
209       return 1;
210     }
211 
212     throw new RuntimeException("unexpected CellScannerPosition:"+position);
213   }
214 
215 }