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.HConstants;
38  import org.apache.hadoop.hbase.KeyValue;
39  import org.apache.hadoop.hbase.Tag;
40  import org.apache.hadoop.hbase.protobuf.generated.ClientProtos;
41  import org.apache.hadoop.hbase.testclassification.SmallTests;
42  import org.apache.hadoop.hbase.util.Bytes;
43  import org.junit.Test;
44  import org.junit.experimental.categories.Category;
45  
46  @Category(SmallTests.class)
47  public class TestResult extends TestCase {
48  
49    private static final Log LOG = LogFactory.getLog(TestResult.class.getName());
50  
51    static KeyValue[] genKVs(final byte[] row, final byte[] family,
52                             final byte[] value,
53                      final long timestamp,
54                      final int cols) {
55      KeyValue [] kvs = new KeyValue[cols];
56  
57      for (int i = 0; i < cols ; i++) {
58        kvs[i] = new KeyValue(
59            row, family, Bytes.toBytes(i),
60            timestamp,
61            Bytes.add(value, Bytes.toBytes(i)));
62      }
63      return kvs;
64    }
65  
66    static final byte [] row = Bytes.toBytes("row");
67    static final byte [] family = Bytes.toBytes("family");
68    static final byte [] value = Bytes.toBytes("value");
69  
70    /**
71     * Run some tests to ensure Result acts like a proper CellScanner.
72     * @throws IOException
73     */
74    public void testResultAsCellScanner() throws IOException {
75      Cell [] cells = genKVs(row, family, value, 1, 10);
76      Arrays.sort(cells, KeyValue.COMPARATOR);
77      Result r = Result.create(cells);
78      assertSame(r, cells);
79      // Assert I run over same result multiple times.
80      assertSame(r.cellScanner(), cells);
81      assertSame(r.cellScanner(), cells);
82      // Assert we are not creating new object when doing cellscanner
83      assertTrue(r == r.cellScanner());
84    }
85  
86    private void assertSame(final CellScanner cellScanner, final Cell [] cells) throws IOException {
87      int count = 0;
88      while (cellScanner.advance()) {
89        assertTrue(cells[count].equals(cellScanner.current()));
90        count++;
91      }
92      assertEquals(cells.length, count);
93    }
94  
95    public void testBasicGetColumn() throws Exception {
96      KeyValue [] kvs = genKVs(row, family, value, 1, 100);
97  
98      Arrays.sort(kvs, KeyValue.COMPARATOR);
99  
100     Result r = Result.create(kvs);
101 
102     for (int i = 0; i < 100; ++i) {
103       final byte[] qf = Bytes.toBytes(i);
104 
105       List<Cell> ks = r.getColumnCells(family, qf);
106       assertEquals(1, ks.size());
107       assertTrue(CellUtil.matchingQualifier(ks.get(0), qf));
108       assertEquals(ks.get(0), r.getColumnLatestCell(family, qf));
109     }
110   }
111 
112   public void testCurrentOnEmptyCell() throws IOException {
113     Result r = Result.create(new Cell[0]);
114     assertFalse(r.advance());
115     assertNull(r.current());
116   }
117 
118   public void testAdvanceTwiceOnEmptyCell() throws IOException {
119     Result r = Result.create(new Cell[0]);
120     assertFalse(r.advance());
121     try {
122       r.advance();
123       fail("NoSuchElementException should have been thrown!");
124     } catch (NoSuchElementException ex) {
125       LOG.debug("As expected: " + ex.getMessage());
126     }
127   }
128 
129   public void testMultiVersionGetColumn() throws Exception {
130     KeyValue [] kvs1 = genKVs(row, family, value, 1, 100);
131     KeyValue [] kvs2 = genKVs(row, family, value, 200, 100);
132 
133     KeyValue [] kvs = new KeyValue[kvs1.length+kvs2.length];
134     System.arraycopy(kvs1, 0, kvs, 0, kvs1.length);
135     System.arraycopy(kvs2, 0, kvs, kvs1.length, kvs2.length);
136 
137     Arrays.sort(kvs, KeyValue.COMPARATOR);
138 
139     Result r = Result.create(kvs);
140     for (int i = 0; i < 100; ++i) {
141       final byte[] qf = Bytes.toBytes(i);
142 
143       List<Cell> ks = r.getColumnCells(family, qf);
144       assertEquals(2, ks.size());
145       assertTrue(CellUtil.matchingQualifier(ks.get(0), qf));
146       assertEquals(200, ks.get(0).getTimestamp());
147       assertEquals(ks.get(0), r.getColumnLatestCell(family, qf));
148     }
149   }
150 
151   public void testBasicGetValue() throws Exception {
152     KeyValue [] kvs = genKVs(row, family, value, 1, 100);
153 
154     Arrays.sort(kvs, KeyValue.COMPARATOR);
155 
156     Result r = Result.create(kvs);
157 
158     for (int i = 0; i < 100; ++i) {
159       final byte[] qf = Bytes.toBytes(i);
160 
161       assertByteEquals(Bytes.add(value, Bytes.toBytes(i)), r.getValue(family, qf));
162       assertTrue(r.containsColumn(family, qf));
163     }
164   }
165 
166   public void testMultiVersionGetValue() throws Exception {
167     KeyValue [] kvs1 = genKVs(row, family, value, 1, 100);
168     KeyValue [] kvs2 = genKVs(row, family, value, 200, 100);
169 
170     KeyValue [] kvs = new KeyValue[kvs1.length+kvs2.length];
171     System.arraycopy(kvs1, 0, kvs, 0, kvs1.length);
172     System.arraycopy(kvs2, 0, kvs, kvs1.length, kvs2.length);
173 
174     Arrays.sort(kvs, KeyValue.COMPARATOR);
175 
176     Result r = Result.create(kvs);
177     for (int i = 0; i < 100; ++i) {
178       final byte[] qf = Bytes.toBytes(i);
179 
180       assertByteEquals(Bytes.add(value, Bytes.toBytes(i)), r.getValue(family, qf));
181       assertTrue(r.containsColumn(family, qf));
182     }
183   }
184 
185   public void testBasicLoadValue() throws Exception {
186     KeyValue [] kvs = genKVs(row, family, value, 1, 100);
187 
188     Arrays.sort(kvs, KeyValue.COMPARATOR);
189 
190     Result r = Result.create(kvs);
191     ByteBuffer loadValueBuffer = ByteBuffer.allocate(1024);
192 
193     for (int i = 0; i < 100; ++i) {
194       final byte[] qf = Bytes.toBytes(i);
195 
196       loadValueBuffer.clear();
197       r.loadValue(family, qf, loadValueBuffer);
198       loadValueBuffer.flip();
199       assertEquals(ByteBuffer.wrap(Bytes.add(value, Bytes.toBytes(i))), loadValueBuffer);
200       assertEquals(ByteBuffer.wrap(Bytes.add(value, Bytes.toBytes(i))),
201           r.getValueAsByteBuffer(family, qf));
202     }
203   }
204 
205   public void testMultiVersionLoadValue() throws Exception {
206     KeyValue [] kvs1 = genKVs(row, family, value, 1, 100);
207     KeyValue [] kvs2 = genKVs(row, family, value, 200, 100);
208 
209     KeyValue [] kvs = new KeyValue[kvs1.length+kvs2.length];
210     System.arraycopy(kvs1, 0, kvs, 0, kvs1.length);
211     System.arraycopy(kvs2, 0, kvs, kvs1.length, kvs2.length);
212 
213     Arrays.sort(kvs, KeyValue.COMPARATOR);
214 
215     ByteBuffer loadValueBuffer = ByteBuffer.allocate(1024);
216 
217     Result r = Result.create(kvs);
218     for (int i = 0; i < 100; ++i) {
219       final byte[] qf = Bytes.toBytes(i);
220 
221       loadValueBuffer.clear();
222       r.loadValue(family, qf, loadValueBuffer);
223       loadValueBuffer.flip();
224       assertEquals(ByteBuffer.wrap(Bytes.add(value, Bytes.toBytes(i))), loadValueBuffer);
225       assertEquals(ByteBuffer.wrap(Bytes.add(value, Bytes.toBytes(i))),
226           r.getValueAsByteBuffer(family, qf));
227     }
228   }
229 
230   /**
231    * Verify that Result.compareResults(...) behaves correctly.
232    */
233   public void testCompareResults() throws Exception {
234     byte [] value1 = Bytes.toBytes("value1");
235     byte [] qual = Bytes.toBytes("qual");
236 
237     KeyValue kv1 = new KeyValue(row, family, qual, value);
238     KeyValue kv2 = new KeyValue(row, family, qual, value1);
239 
240     Result r1 = Result.create(new KeyValue[] {kv1});
241     Result r2 = Result.create(new KeyValue[] {kv2});
242     // no exception thrown
243     Result.compareResults(r1, r1);
244     try {
245       // these are different (HBASE-4800)
246       Result.compareResults(r1, r2);
247       fail();
248     } catch (Exception x) {
249       assertTrue(x.getMessage().startsWith("This result was different:"));
250     }
251   }
252 
253   /**
254    *  Test Result#compareResults when tags are present.
255    * @throws Exception Exception
256    */
257   @Test
258   public void testCompareResultsWithTags() throws Exception {
259     byte[] qualifier = Bytes.toBytes("q");
260     String tagValue1 = "tagV1";
261     String tagValue2 = "tagV2";
262     // Create cell with 2 tags
263     KeyValue kv1 = new KeyValue(row, family, qualifier, HConstants.LATEST_TIMESTAMP,
264       value, new Tag[] {new Tag((byte) 1, tagValue1), new Tag((byte) 2, tagValue2)});
265 
266     // Create cell with no tags.
267     KeyValue kv2 = new KeyValue(row, family, qualifier, HConstants.LATEST_TIMESTAMP,
268       value);
269 
270     Result r1 = Result.create(new KeyValue[] {kv1});
271     Result r2 = Result.create(new KeyValue[] {kv2});
272     // Compare result with self.
273     Result.compareResults(r1, r1);
274 
275     try {
276       // Compare result with 2 tags v/s no tags.
277       Result.compareResults(r1, r2);
278       fail();
279     } catch (Exception e) {
280       assertTrue(e.getMessage().startsWith("This result was different:"));
281     }
282 
283     // Create cell with just 1 tag.
284     String tagValue3 = "tagV3";
285     KeyValue kv3 = new KeyValue(row, family, qualifier, HConstants.LATEST_TIMESTAMP,
286       value, new Tag[] {new Tag((byte) 1, tagValue3)});
287     Result r3 = Result.create(new KeyValue[] {kv3});
288     try {
289       // Compare result with 2 tags v/s 1 tag.
290       Result.compareResults(r1, r3);
291       fail();
292     } catch (Exception e) {
293       assertTrue(e.getMessage().startsWith("This result was different:"));
294     }
295   }
296 
297   /**
298    * Verifies that one can't modify instance of EMPTY_RESULT.
299    */
300   public void testEmptyResultIsReadonly() {
301     Result emptyResult = Result.EMPTY_RESULT;
302     Result otherResult = new Result();
303 
304     try {
305       emptyResult.copyFrom(otherResult);
306       fail("UnsupportedOperationException should have been thrown!");
307     } catch (UnsupportedOperationException ex) {
308       LOG.debug("As expected: " + ex.getMessage());
309     }
310     try {
311       emptyResult.addResults(ClientProtos.RegionLoadStats.getDefaultInstance());
312       fail("UnsupportedOperationException should have been thrown!");
313     } catch (UnsupportedOperationException ex) {
314       LOG.debug("As expected: " + ex.getMessage());
315     }
316     try {
317       emptyResult.setExists(true);
318       fail("UnsupportedOperationException should have been thrown!");
319     } catch (UnsupportedOperationException ex) {
320       LOG.debug("As expected: " + ex.getMessage());
321     }
322   }
323 
324   /**
325    * Microbenchmark that compares {@link Result#getValue} and {@link Result#loadValue} performance.
326    *
327    * @throws Exception
328    */
329   public void doReadBenchmark() throws Exception {
330 
331     final int n = 5;
332     final int m = 100000000;
333 
334     StringBuilder valueSB = new StringBuilder();
335     for (int i = 0; i < 100; i++) {
336       valueSB.append((byte)(Math.random() * 10));
337     }
338 
339     StringBuilder rowSB = new StringBuilder();
340     for (int i = 0; i < 50; i++) {
341       rowSB.append((byte)(Math.random() * 10));
342     }
343 
344     KeyValue [] kvs = genKVs(Bytes.toBytes(rowSB.toString()), family,
345         Bytes.toBytes(valueSB.toString()), 1, n);
346     Arrays.sort(kvs, KeyValue.COMPARATOR);
347     ByteBuffer loadValueBuffer = ByteBuffer.allocate(1024);
348     Result r = Result.create(kvs);
349 
350     byte[][] qfs = new byte[n][Bytes.SIZEOF_INT];
351     for (int i = 0; i < n; ++i) {
352       System.arraycopy(qfs[i], 0, Bytes.toBytes(i), 0, Bytes.SIZEOF_INT);
353     }
354 
355     // warm up
356     for (int k = 0; k < 100000; k++) {
357       for (int i = 0; i < n; ++i) {
358         r.getValue(family, qfs[i]);
359         loadValueBuffer.clear();
360         r.loadValue(family, qfs[i], loadValueBuffer);
361         loadValueBuffer.flip();
362       }
363     }
364 
365     System.gc();
366     long start = System.nanoTime();
367     for (int k = 0; k < m; k++) {
368       for (int i = 0; i < n; ++i) {
369         loadValueBuffer.clear();
370         r.loadValue(family, qfs[i], loadValueBuffer);
371         loadValueBuffer.flip();
372       }
373     }
374     long stop = System.nanoTime();
375     System.out.println("loadValue(): " + (stop - start));
376 
377     System.gc();
378     start = System.nanoTime();
379     for (int k = 0; k < m; k++) {
380       for (int i = 0; i < n; i++) {
381         r.getValue(family, qfs[i]);
382       }
383     }
384     stop = System.nanoTime();
385     System.out.println("getValue():  " + (stop - start));
386   }
387 
388   /**
389    * Calls non-functional test methods.
390    *
391    * @param args
392    */
393   public static void main(String[] args) {
394     TestResult testResult = new TestResult();
395     try {
396       testResult.doReadBenchmark();
397     } catch (Exception e) {
398       LOG.error("Unexpected exception", e);
399     }
400   }
401 }