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