View Javadoc

1   /*
2    *
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *     http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the License for the specific language governing permissions and
17   * limitations under the License.
18   */
19  
20  package org.apache.hadoop.hbase.client;
21  
22  import static org.apache.hadoop.hbase.HBaseTestCase.assertByteEquals;
23  
24  import java.io.IOException;
25  import java.nio.ByteBuffer;
26  import java.util.Arrays;
27  import java.util.List;
28  import java.util.NoSuchElementException;
29  
30  import junit.framework.TestCase;
31  
32  import org.apache.commons.logging.Log;
33  import org.apache.commons.logging.LogFactory;
34  import org.apache.hadoop.hbase.Cell;
35  import org.apache.hadoop.hbase.CellScanner;
36  import org.apache.hadoop.hbase.CellUtil;
37  import org.apache.hadoop.hbase.KeyValue;
38  import org.apache.hadoop.hbase.protobuf.generated.ClientProtos;
39  import org.apache.hadoop.hbase.testclassification.SmallTests;
40  import org.apache.hadoop.hbase.util.Bytes;
41  import org.junit.experimental.categories.Category;
42  
43  @Category(SmallTests.class)
44  public class TestResult extends TestCase {
45  
46    private static final Log LOG = LogFactory.getLog(TestResult.class.getName());
47  
48    static KeyValue[] genKVs(final byte[] row, final byte[] family,
49                             final byte[] value,
50                      final long timestamp,
51                      final int cols) {
52      KeyValue [] kvs = new KeyValue[cols];
53  
54      for (int i = 0; i < cols ; i++) {
55        kvs[i] = new KeyValue(
56            row, family, Bytes.toBytes(i),
57            timestamp,
58            Bytes.add(value, Bytes.toBytes(i)));
59      }
60      return kvs;
61    }
62  
63    static final byte [] row = Bytes.toBytes("row");
64    static final byte [] family = Bytes.toBytes("family");
65    static final byte [] value = Bytes.toBytes("value");
66  
67    /**
68     * Run some tests to ensure Result acts like a proper CellScanner.
69     * @throws IOException
70     */
71    public void testResultAsCellScanner() throws IOException {
72      Cell [] cells = genKVs(row, family, value, 1, 10);
73      Arrays.sort(cells, KeyValue.COMPARATOR);
74      Result r = Result.create(cells);
75      assertSame(r, cells);
76      // Assert I run over same result multiple times.
77      assertSame(r.cellScanner(), cells);
78      assertSame(r.cellScanner(), cells);
79      // Assert we are not creating new object when doing cellscanner
80      assertTrue(r == r.cellScanner());
81    }
82  
83    private void assertSame(final CellScanner cellScanner, final Cell [] cells) throws IOException {
84      int count = 0;
85      while (cellScanner.advance()) {
86        assertTrue(cells[count].equals(cellScanner.current()));
87        count++;
88      }
89      assertEquals(cells.length, count);
90    }
91  
92    public void testBasicGetColumn() throws Exception {
93      KeyValue [] kvs = genKVs(row, family, value, 1, 100);
94  
95      Arrays.sort(kvs, KeyValue.COMPARATOR);
96  
97      Result r = Result.create(kvs);
98  
99      for (int i = 0; i < 100; ++i) {
100       final byte[] qf = Bytes.toBytes(i);
101 
102       List<Cell> ks = r.getColumnCells(family, qf);
103       assertEquals(1, ks.size());
104       assertTrue(CellUtil.matchingQualifier(ks.get(0), qf));
105       assertEquals(ks.get(0), r.getColumnLatestCell(family, qf));
106     }
107   }
108 
109   public void testCurrentOnEmptyCell() throws IOException {
110     Result r = Result.create(new Cell[0]);
111     assertFalse(r.advance());
112     assertNull(r.current());
113   }
114 
115   public void testAdvanceTwiceOnEmptyCell() throws IOException {
116     Result r = Result.create(new Cell[0]);
117     assertFalse(r.advance());
118     try {
119       r.advance();
120       fail("NoSuchElementException should have been thrown!");
121     } catch (NoSuchElementException ex) {
122       LOG.debug("As expected: " + ex.getMessage());
123     }
124   }
125 
126   public void testMultiVersionGetColumn() throws Exception {
127     KeyValue [] kvs1 = genKVs(row, family, value, 1, 100);
128     KeyValue [] kvs2 = genKVs(row, family, value, 200, 100);
129 
130     KeyValue [] kvs = new KeyValue[kvs1.length+kvs2.length];
131     System.arraycopy(kvs1, 0, kvs, 0, kvs1.length);
132     System.arraycopy(kvs2, 0, kvs, kvs1.length, kvs2.length);
133 
134     Arrays.sort(kvs, KeyValue.COMPARATOR);
135 
136     Result r = Result.create(kvs);
137     for (int i = 0; i < 100; ++i) {
138       final byte[] qf = Bytes.toBytes(i);
139 
140       List<Cell> ks = r.getColumnCells(family, qf);
141       assertEquals(2, ks.size());
142       assertTrue(CellUtil.matchingQualifier(ks.get(0), qf));
143       assertEquals(200, ks.get(0).getTimestamp());
144       assertEquals(ks.get(0), r.getColumnLatestCell(family, qf));
145     }
146   }
147 
148   public void testBasicGetValue() throws Exception {
149     KeyValue [] kvs = genKVs(row, family, value, 1, 100);
150 
151     Arrays.sort(kvs, KeyValue.COMPARATOR);
152 
153     Result r = Result.create(kvs);
154 
155     for (int i = 0; i < 100; ++i) {
156       final byte[] qf = Bytes.toBytes(i);
157 
158       assertByteEquals(Bytes.add(value, Bytes.toBytes(i)), r.getValue(family, qf));
159       assertTrue(r.containsColumn(family, qf));
160     }
161   }
162 
163   public void testMultiVersionGetValue() throws Exception {
164     KeyValue [] kvs1 = genKVs(row, family, value, 1, 100);
165     KeyValue [] kvs2 = genKVs(row, family, value, 200, 100);
166 
167     KeyValue [] kvs = new KeyValue[kvs1.length+kvs2.length];
168     System.arraycopy(kvs1, 0, kvs, 0, kvs1.length);
169     System.arraycopy(kvs2, 0, kvs, kvs1.length, kvs2.length);
170 
171     Arrays.sort(kvs, KeyValue.COMPARATOR);
172 
173     Result r = Result.create(kvs);
174     for (int i = 0; i < 100; ++i) {
175       final byte[] qf = Bytes.toBytes(i);
176 
177       assertByteEquals(Bytes.add(value, Bytes.toBytes(i)), r.getValue(family, qf));
178       assertTrue(r.containsColumn(family, qf));
179     }
180   }
181 
182   public void testBasicLoadValue() throws Exception {
183     KeyValue [] kvs = genKVs(row, family, value, 1, 100);
184 
185     Arrays.sort(kvs, KeyValue.COMPARATOR);
186 
187     Result r = Result.create(kvs);
188     ByteBuffer loadValueBuffer = ByteBuffer.allocate(1024);
189 
190     for (int i = 0; i < 100; ++i) {
191       final byte[] qf = Bytes.toBytes(i);
192 
193       loadValueBuffer.clear();
194       r.loadValue(family, qf, loadValueBuffer);
195       loadValueBuffer.flip();
196       assertEquals(ByteBuffer.wrap(Bytes.add(value, Bytes.toBytes(i))), loadValueBuffer);
197       assertEquals(ByteBuffer.wrap(Bytes.add(value, Bytes.toBytes(i))),
198           r.getValueAsByteBuffer(family, qf));
199     }
200   }
201 
202   public void testMultiVersionLoadValue() throws Exception {
203     KeyValue [] kvs1 = genKVs(row, family, value, 1, 100);
204     KeyValue [] kvs2 = genKVs(row, family, value, 200, 100);
205 
206     KeyValue [] kvs = new KeyValue[kvs1.length+kvs2.length];
207     System.arraycopy(kvs1, 0, kvs, 0, kvs1.length);
208     System.arraycopy(kvs2, 0, kvs, kvs1.length, kvs2.length);
209 
210     Arrays.sort(kvs, KeyValue.COMPARATOR);
211 
212     ByteBuffer loadValueBuffer = ByteBuffer.allocate(1024);
213 
214     Result r = Result.create(kvs);
215     for (int i = 0; i < 100; ++i) {
216       final byte[] qf = Bytes.toBytes(i);
217 
218       loadValueBuffer.clear();
219       r.loadValue(family, qf, loadValueBuffer);
220       loadValueBuffer.flip();
221       assertEquals(ByteBuffer.wrap(Bytes.add(value, Bytes.toBytes(i))), loadValueBuffer);
222       assertEquals(ByteBuffer.wrap(Bytes.add(value, Bytes.toBytes(i))),
223           r.getValueAsByteBuffer(family, qf));
224     }
225   }
226 
227   /**
228    * Verify that Result.compareResults(...) behaves correctly.
229    */
230   public void testCompareResults() throws Exception {
231     byte [] value1 = Bytes.toBytes("value1");
232     byte [] qual = Bytes.toBytes("qual");
233 
234     KeyValue kv1 = new KeyValue(row, family, qual, value);
235     KeyValue kv2 = new KeyValue(row, family, qual, value1);
236 
237     Result r1 = Result.create(new KeyValue[] {kv1});
238     Result r2 = Result.create(new KeyValue[] {kv2});
239     // no exception thrown
240     Result.compareResults(r1, r1);
241     try {
242       // these are different (HBASE-4800)
243       Result.compareResults(r1, r2);
244       fail();
245     } catch (Exception x) {
246       assertTrue(x.getMessage().startsWith("This result was different:"));
247     }
248   }
249 
250   /**
251    * Verifies that one can't modify instance of EMPTY_RESULT.
252    */
253   public void testEmptyResultIsReadonly() {
254     Result emptyResult = Result.EMPTY_RESULT;
255     Result otherResult = new Result();
256 
257     try {
258       emptyResult.copyFrom(otherResult);
259       fail("UnsupportedOperationException should have been thrown!");
260     } catch (UnsupportedOperationException ex) {
261       LOG.debug("As expected: " + ex.getMessage());
262     }
263     try {
264       emptyResult.addResults(ClientProtos.RegionLoadStats.getDefaultInstance());
265       fail("UnsupportedOperationException should have been thrown!");
266     } catch (UnsupportedOperationException ex) {
267       LOG.debug("As expected: " + ex.getMessage());
268     }
269     try {
270       emptyResult.setExists(true);
271       fail("UnsupportedOperationException should have been thrown!");
272     } catch (UnsupportedOperationException ex) {
273       LOG.debug("As expected: " + ex.getMessage());
274     }
275   }
276 
277   /**
278    * Microbenchmark that compares {@link Result#getValue} and {@link Result#loadValue} performance.
279    *
280    * @throws Exception
281    */
282   public void doReadBenchmark() throws Exception {
283 
284     final int n = 5;
285     final int m = 100000000;
286 
287     StringBuilder valueSB = new StringBuilder();
288     for (int i = 0; i < 100; i++) {
289       valueSB.append((byte)(Math.random() * 10));
290     }
291 
292     StringBuilder rowSB = new StringBuilder();
293     for (int i = 0; i < 50; i++) {
294       rowSB.append((byte)(Math.random() * 10));
295     }
296 
297     KeyValue [] kvs = genKVs(Bytes.toBytes(rowSB.toString()), family,
298         Bytes.toBytes(valueSB.toString()), 1, n);
299     Arrays.sort(kvs, KeyValue.COMPARATOR);
300     ByteBuffer loadValueBuffer = ByteBuffer.allocate(1024);
301     Result r = Result.create(kvs);
302 
303     byte[][] qfs = new byte[n][Bytes.SIZEOF_INT];
304     for (int i = 0; i < n; ++i) {
305       System.arraycopy(qfs[i], 0, Bytes.toBytes(i), 0, Bytes.SIZEOF_INT);
306     }
307 
308     // warm up
309     for (int k = 0; k < 100000; k++) {
310       for (int i = 0; i < n; ++i) {
311         r.getValue(family, qfs[i]);
312         loadValueBuffer.clear();
313         r.loadValue(family, qfs[i], loadValueBuffer);
314         loadValueBuffer.flip();
315       }
316     }
317 
318     System.gc();
319     long start = System.nanoTime();
320     for (int k = 0; k < m; k++) {
321       for (int i = 0; i < n; ++i) {
322         loadValueBuffer.clear();
323         r.loadValue(family, qfs[i], loadValueBuffer);
324         loadValueBuffer.flip();
325       }
326     }
327     long stop = System.nanoTime();
328     System.out.println("loadValue(): " + (stop - start));
329 
330     System.gc();
331     start = System.nanoTime();
332     for (int k = 0; k < m; k++) {
333       for (int i = 0; i < n; i++) {
334         r.getValue(family, qfs[i]);
335       }
336     }
337     stop = System.nanoTime();
338     System.out.println("getValue():  " + (stop - start));
339   }
340 
341   /**
342    * Calls non-functional test methods.
343    *
344    * @param args
345    */
346   public static void main(String[] args) {
347     TestResult testResult = new TestResult();
348     try {
349       testResult.doReadBenchmark();
350     } catch (Exception e) {
351       LOG.error("Unexpected exception", e);
352     }
353   }
354 }