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