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.hbase.Cell;
24  import org.apache.hadoop.hbase.CellComparator;
25  import org.apache.hadoop.hbase.CellUtil;
26  import org.apache.hadoop.hbase.KeyValue.Type;
27  import org.apache.hadoop.hbase.KeyValueUtil;
28  import org.apache.hadoop.hbase.SettableSequenceId;
29  import org.apache.hadoop.hbase.classification.InterfaceAudience;
30  import org.apache.hadoop.hbase.codec.prefixtree.decode.DecoderFactory;
31  import org.apache.hadoop.hbase.codec.prefixtree.decode.PrefixTreeArraySearcher;
32  import org.apache.hadoop.hbase.codec.prefixtree.scanner.CellScannerPosition;
33  import org.apache.hadoop.hbase.io.HeapSize;
34  import org.apache.hadoop.hbase.io.encoding.DataBlockEncoder.EncodedSeeker;
35  import org.apache.hadoop.hbase.nio.ByteBuff;
36  import org.apache.hadoop.hbase.util.Bytes;
37  import org.apache.hadoop.hbase.util.ClassSize;
38  
39  /**
40   * These methods have the same definition as any implementation of the EncodedSeeker.
41   *
42   * In the future, the EncodedSeeker could be modified to work with the Cell interface directly.  It
43   * currently returns a new KeyValue object each time getKeyValue is called.  This is not horrible,
44   * but in order to create a new KeyValue object, we must first allocate a new byte[] and copy in
45   * the data from the PrefixTreeCell.  It is somewhat heavyweight right now.
46   */
47  @InterfaceAudience.Private
48  public class PrefixTreeSeeker implements EncodedSeeker {
49  
50    protected ByteBuffer block;
51    protected boolean includeMvccVersion;
52    protected PrefixTreeArraySearcher ptSearcher;
53  
54    public PrefixTreeSeeker(boolean includeMvccVersion) {
55      this.includeMvccVersion = includeMvccVersion;
56    }
57  
58    @Override
59    public void setCurrentBuffer(ByteBuff fullBlockBuffer) {
60      block = fullBlockBuffer.asSubByteBuffer(fullBlockBuffer.limit());
61      // TODO : change to Bytebuff
62      ptSearcher = DecoderFactory.checkOut(block, includeMvccVersion);
63      rewind();
64    }
65  
66    /**
67     * <p>
68     * Currently unused.
69     * </p>
70     * TODO performance leak. should reuse the searchers. hbase does not currently have a hook where
71     * this can be called
72     */
73    public void releaseCurrentSearcher(){
74      DecoderFactory.checkIn(ptSearcher);
75    }
76  
77  
78    @Override
79    public Cell getKey() {
80      return ptSearcher.current();
81    }
82  
83  
84    @Override
85    public ByteBuffer getValueShallowCopy() {
86      return CellUtil.getValueBufferShallowCopy(ptSearcher.current());
87    }
88  
89    /**
90     * currently must do deep copy into new array
91     */
92    @Override
93    public ByteBuffer getKeyValueBuffer() {
94      return KeyValueUtil.copyToNewByteBuffer(ptSearcher.current());
95    }
96  
97    /**
98     * currently must do deep copy into new array
99     */
100   @Override
101   public Cell getCell() {
102     Cell cell = ptSearcher.current();
103     if (cell == null) {
104       return null;
105     }
106     return new ClonedPrefixTreeCell(cell.getRowArray(), cell.getRowOffset(), cell.getRowLength(),
107         cell.getFamilyArray(), cell.getFamilyOffset(), cell.getFamilyLength(),
108         cell.getQualifierArray(), cell.getQualifierOffset(), cell.getQualifierLength(),
109         cell.getValueArray(), cell.getValueOffset(), cell.getValueLength(), cell.getTagsArray(),
110         cell.getTagsOffset(), cell.getTagsLength(), cell.getTimestamp(), cell.getTypeByte(),
111         cell.getSequenceId());
112   }
113 
114   /**
115    * <p>
116    * Currently unused.
117    * </p><p>
118    * A nice, lightweight reference, though the underlying cell is transient. This method may return
119    * the same reference to the backing PrefixTreeCell repeatedly, while other implementations may
120    * return a different reference for each Cell.
121    * </p>
122    * The goal will be to transition the upper layers of HBase, like Filters and KeyValueHeap, to
123    * use this method instead of the getKeyValue() methods above.
124    */
125   public Cell get() {
126     return ptSearcher.current();
127   }
128 
129   @Override
130   public void rewind() {
131     ptSearcher.positionAtFirstCell();
132   }
133 
134   @Override
135   public boolean next() {
136     return ptSearcher.advance();
137   }
138 
139   public boolean advance() {
140     return ptSearcher.advance();
141   }
142 
143 
144   private static final boolean USE_POSITION_BEFORE = false;
145 
146   /*
147    * Support both of these options since the underlying PrefixTree supports
148    * both. Possibly expand the EncodedSeeker to utilize them both.
149    */
150 
151   protected int seekToOrBeforeUsingPositionAtOrBefore(Cell kv, boolean seekBefore) {
152     // this does a deep copy of the key byte[] because the CellSearcher
153     // interface wants a Cell
154     CellScannerPosition position = ptSearcher.seekForwardToOrBefore(kv);
155 
156     if (CellScannerPosition.AT == position) {
157       if (seekBefore) {
158         ptSearcher.previous();
159         return 1;
160       }
161       return 0;
162     }
163 
164     return 1;
165   }
166 
167   protected int seekToOrBeforeUsingPositionAtOrAfter(Cell kv, boolean seekBefore) {
168     // should probably switch this to use the seekForwardToOrBefore method
169     CellScannerPosition position = ptSearcher.seekForwardToOrAfter(kv);
170 
171     if (CellScannerPosition.AT == position) {
172       if (seekBefore) {
173         ptSearcher.previous();
174         return 1;
175       }
176       return 0;
177 
178     }
179 
180     if (CellScannerPosition.AFTER == position) {
181       if (!ptSearcher.isBeforeFirst()) {
182         ptSearcher.previous();
183       }
184       return 1;
185     }
186 
187     if (position == CellScannerPosition.AFTER_LAST) {
188       if (seekBefore) {
189         ptSearcher.previous();
190       }
191       return 1;
192     }
193 
194     throw new RuntimeException("unexpected CellScannerPosition:" + position);
195   }
196 
197   @Override
198   public int seekToKeyInBlock(Cell key, boolean forceBeforeOnExactMatch) {
199     if (USE_POSITION_BEFORE) {
200       return seekToOrBeforeUsingPositionAtOrBefore(key, forceBeforeOnExactMatch);
201     } else {
202       return seekToOrBeforeUsingPositionAtOrAfter(key, forceBeforeOnExactMatch);
203     }
204   }
205 
206   @Override
207   public int compareKey(CellComparator comparator, Cell key) {
208     return comparator.compare(key,
209         ptSearcher.current());
210   }
211   /**
212    * Cloned version of the PrefixTreeCell where except the value part, the rest
213    * of the key part is deep copied
214    *
215    */
216   private static class ClonedPrefixTreeCell implements Cell, SettableSequenceId, HeapSize {
217     private static final long FIXED_OVERHEAD = ClassSize.align(ClassSize.OBJECT
218         + (5 * ClassSize.REFERENCE) + (2 * Bytes.SIZEOF_LONG) + (4 * Bytes.SIZEOF_INT)
219         + (Bytes.SIZEOF_SHORT) + (2 * Bytes.SIZEOF_BYTE) + (5 * ClassSize.ARRAY));
220     private byte[] row;
221     private short rowLength;
222     private byte[] fam;
223     private byte famLength;
224     private byte[] qual;
225     private int qualLength;
226     private byte[] val;
227     private int valOffset;
228     private int valLength;
229     private byte[] tag;
230     private int tagsLength;
231     private long ts;
232     private long seqId;
233     private byte type;
234 
235     public ClonedPrefixTreeCell(byte[] row, int rowOffset, short rowLength, byte[] fam,
236         int famOffset, byte famLength, byte[] qual, int qualOffset, int qualLength, byte[] val,
237         int valOffset, int valLength, byte[] tag, int tagOffset, int tagLength, long ts, byte type,
238         long seqId) {
239       this.row = new byte[rowLength];
240       System.arraycopy(row, rowOffset, this.row, 0, rowLength);
241       this.rowLength = rowLength;
242       this.fam = new byte[famLength];
243       System.arraycopy(fam, famOffset, this.fam, 0, famLength);
244       this.famLength = famLength;
245       this.qual = new byte[qualLength];
246       System.arraycopy(qual, qualOffset, this.qual, 0, qualLength);
247       this.qualLength = qualLength;
248       this.tag = new byte[tagLength];
249       System.arraycopy(tag, tagOffset, this.tag, 0, tagLength);
250       this.tagsLength = tagLength;
251       this.val = val;
252       this.valLength = valLength;
253       this.valOffset = valOffset;
254       this.ts = ts;
255       this.seqId = seqId;
256       this.type = type;
257     }
258 
259     @Override
260     public void setSequenceId(long seqId) {
261       this.seqId = seqId;
262     }
263 
264     @Override
265     public byte[] getRowArray() {
266       return this.row;
267     }
268 
269     @Override
270     public int getRowOffset() {
271       return 0;
272     }
273 
274     @Override
275     public short getRowLength() {
276       return this.rowLength;
277     }
278 
279     @Override
280     public byte[] getFamilyArray() {
281       return this.fam;
282     }
283 
284     @Override
285     public int getFamilyOffset() {
286       return 0;
287     }
288 
289     @Override
290     public byte getFamilyLength() {
291       return this.famLength;
292     }
293 
294     @Override
295     public byte[] getQualifierArray() {
296       return this.qual;
297     }
298 
299     @Override
300     public int getQualifierOffset() {
301       return 0;
302     }
303 
304     @Override
305     public int getQualifierLength() {
306       return this.qualLength;
307     }
308 
309     @Override
310     public long getTimestamp() {
311       return ts;
312     }
313 
314     @Override
315     public byte getTypeByte() {
316       return type;
317     }
318 
319     @Override
320     public long getSequenceId() {
321       return seqId;
322     }
323 
324     @Override
325     public byte[] getValueArray() {
326       return val;
327     }
328 
329     @Override
330     public int getValueOffset() {
331       return this.valOffset;
332     }
333 
334     @Override
335     public int getValueLength() {
336       return this.valLength;
337     }
338 
339     @Override
340     public byte[] getTagsArray() {
341       return this.tag;
342     }
343 
344     @Override
345     public int getTagsOffset() {
346       return 0;
347     }
348 
349     @Override
350     public int getTagsLength() {
351       return this.tagsLength;
352     }
353 
354     @Override
355     public String toString() {
356       String row = Bytes.toStringBinary(getRowArray(), getRowOffset(), getRowLength());
357       String family = Bytes.toStringBinary(getFamilyArray(), getFamilyOffset(), getFamilyLength());
358       String qualifier = Bytes.toStringBinary(getQualifierArray(), getQualifierOffset(),
359           getQualifierLength());
360       String timestamp = String.valueOf((getTimestamp()));
361       return row + "/" + family + (family != null && family.length() > 0 ? ":" : "") + qualifier
362           + "/" + timestamp + "/" + Type.codeToType(type);
363     }
364 
365     @Override
366     public long heapSize() {
367       return FIXED_OVERHEAD + rowLength + famLength + qualLength + valLength + tagsLength;
368     }
369   }
370 }