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.row;
20  
21  import java.io.ByteArrayOutputStream;
22  import java.io.IOException;
23  import java.nio.ByteBuffer;
24  import java.util.Collection;
25  import java.util.List;
26  
27  import org.apache.hadoop.hbase.Cell;
28  import org.apache.hadoop.hbase.CellComparator;
29  import org.apache.hadoop.hbase.KeyValue;
30  import org.apache.hadoop.hbase.KeyValueUtil;
31  import org.apache.hadoop.hbase.codec.prefixtree.decode.DecoderFactory;
32  import org.apache.hadoop.hbase.codec.prefixtree.encode.PrefixTreeEncoder;
33  import org.apache.hadoop.hbase.codec.prefixtree.scanner.CellScannerPosition;
34  import org.apache.hadoop.hbase.codec.prefixtree.scanner.CellSearcher;
35  import org.apache.hadoop.hbase.util.CollectionUtils;
36  import org.junit.Assert;
37  import org.junit.Test;
38  import org.junit.runner.RunWith;
39  import org.junit.runners.Parameterized;
40  import org.junit.runners.Parameterized.Parameters;
41  
42  @RunWith(Parameterized.class)
43  public class TestPrefixTreeSearcher {
44  
45  	protected static int BLOCK_START = 7;
46  
47    @Parameters
48    public static Collection<Object[]> parameters() {
49      return new TestRowData.InMemory().getAllAsObjectArray();
50    }
51  
52    protected TestRowData rows;
53    protected ByteBuffer block;
54  
55    public TestPrefixTreeSearcher(TestRowData testRows) throws IOException {
56      this.rows = testRows;
57      ByteArrayOutputStream os = new ByteArrayOutputStream(1 << 20);
58      PrefixTreeEncoder kvBuilder = new PrefixTreeEncoder(os, true);
59      for (KeyValue kv : rows.getInputs()) {
60        kvBuilder.write(kv);
61      }
62      kvBuilder.flush();
63      byte[] outputBytes = os.toByteArray();
64      this.block = ByteBuffer.wrap(outputBytes);
65    }
66  
67  
68    @Test
69    public void testScanForwards() throws IOException {
70      CellSearcher searcher = null;
71      try {
72        searcher = DecoderFactory.checkOut(block, true);
73  
74        int i = -1;
75        while (searcher.advance()) {
76          ++i;
77          KeyValue inputCell = rows.getInputs().get(i);
78          Cell outputCell = searcher.current();
79  
80          // check all 3 permutations of equals()
81          Assert.assertEquals(inputCell, outputCell);
82          Assert.assertEquals(outputCell, inputCell);
83          Assert.assertTrue(CellComparator.equals(inputCell, outputCell));
84        }
85        Assert.assertEquals(rows.getInputs().size(), i + 1);
86      } finally {
87        DecoderFactory.checkIn(searcher);
88      }
89    }
90  
91  
92    @Test
93    public void testScanBackwards() throws IOException {
94      CellSearcher searcher = null;
95      try {
96        searcher = DecoderFactory.checkOut(block, true);
97        searcher.positionAfterLastCell();
98        int i = -1;
99        while (searcher.previous()) {
100         ++i;
101         int oppositeIndex = rows.getInputs().size() - i - 1;
102         KeyValue inputKv = rows.getInputs().get(oppositeIndex);
103         KeyValue outputKv = KeyValueUtil.copyToNewKeyValue(searcher.current());
104         Assert.assertEquals(inputKv, outputKv);
105       }
106       Assert.assertEquals(rows.getInputs().size(), i + 1);
107     } finally {
108       DecoderFactory.checkIn(searcher);
109     }
110   }
111 
112 
113   @Test
114   public void testRandomSeekHits() throws IOException {
115     CellSearcher searcher = null;
116     try {
117       searcher = DecoderFactory.checkOut(block, true);
118       for (KeyValue kv : rows.getInputs()) {
119         boolean hit = searcher.positionAt(kv);
120         Assert.assertTrue(hit);
121         Cell foundKv = searcher.current();
122         Assert.assertTrue(CellComparator.equals(kv, foundKv));
123       }
124     } finally {
125       DecoderFactory.checkIn(searcher);
126     }
127   }
128 
129   @Test
130   public void testRandomSeekMisses() throws IOException {
131     CellSearcher searcher = null;
132     List<Integer> rowStartIndexes = rows.getRowStartIndexes();
133     try {
134       searcher = DecoderFactory.checkOut(block, true);
135 
136       //test both the positionAtOrBefore and positionAtOrAfter methods
137       for(boolean beforeVsAfterOnMiss : new boolean[]{true, false}){
138         for (int i=0; i < rows.getInputs().size(); ++i) {
139           KeyValue kv = rows.getInputs().get(i);
140 
141           //nextRow
142           KeyValue inputNextRow = KeyValueUtil.createFirstKeyInNextRow(kv);
143 
144           CellScannerPosition position = beforeVsAfterOnMiss
145               ? searcher.positionAtOrBefore(inputNextRow)
146               : searcher.positionAtOrAfter(inputNextRow);
147           boolean isFirstInRow = rowStartIndexes.contains(i);
148           if(isFirstInRow){
149             int rowIndex = rowStartIndexes.indexOf(i);
150             if(rowIndex < rowStartIndexes.size() - 1){
151               if(beforeVsAfterOnMiss){
152                 Assert.assertEquals(CellScannerPosition.BEFORE, position);
153               }else{
154                 Assert.assertEquals(CellScannerPosition.AFTER, position);
155               }
156 
157               int expectedInputIndex = beforeVsAfterOnMiss
158                   ? rowStartIndexes.get(rowIndex + 1) - 1
159                   : rowStartIndexes.get(rowIndex + 1);
160               Assert.assertEquals(rows.getInputs().get(expectedInputIndex), searcher.current());
161             }
162           }
163 
164           //previous KV
165           KeyValue inputPreviousKv = KeyValueUtil.previousKey(kv);
166           boolean hit = searcher.positionAt(inputPreviousKv);
167           Assert.assertFalse(hit);
168           position = searcher.positionAtOrAfter(inputPreviousKv);
169           if(CollectionUtils.isLastIndex(rows.getInputs(), i)){
170             Assert.assertTrue(CellScannerPosition.AFTER_LAST == position);
171           }else{
172             Assert.assertTrue(CellScannerPosition.AFTER == position);
173             /*
174              * TODO: why i+1 instead of i?
175              */
176             Assert.assertEquals(rows.getInputs().get(i+1), searcher.current());
177           }
178         }
179       }
180     } finally {
181       DecoderFactory.checkIn(searcher);
182     }
183   }
184 
185 
186   @Test
187   public void testRandomSeekIndividualAssertions() throws IOException {
188     CellSearcher searcher = null;
189     try {
190       searcher = DecoderFactory.checkOut(block, true);
191       rows.individualSearcherAssertions(searcher);
192     } finally {
193       DecoderFactory.checkIn(searcher);
194     }
195   }
196 }