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 testAdvanceMultipleOnEmptyCell() throws IOException {
119     Result r = Result.create(new Cell[0]);
120     // After HBASE-26688, advance of result with empty cell list will always return false.
121     // Here 10 is an arbitrary number to test the logic.
122     for (int i = 0; i < 10; i++) {
123       assertFalse(r.advance());
124     }
125   }
126 
127   public void testMultiVersionGetColumn() throws Exception {
128     KeyValue [] kvs1 = genKVs(row, family, value, 1, 100);
129     KeyValue [] kvs2 = genKVs(row, family, value, 200, 100);
130 
131     KeyValue [] kvs = new KeyValue[kvs1.length+kvs2.length];
132     System.arraycopy(kvs1, 0, kvs, 0, kvs1.length);
133     System.arraycopy(kvs2, 0, kvs, kvs1.length, kvs2.length);
134 
135     Arrays.sort(kvs, KeyValue.COMPARATOR);
136 
137     Result r = Result.create(kvs);
138     for (int i = 0; i < 100; ++i) {
139       final byte[] qf = Bytes.toBytes(i);
140 
141       List<Cell> ks = r.getColumnCells(family, qf);
142       assertEquals(2, ks.size());
143       assertTrue(CellUtil.matchingQualifier(ks.get(0), qf));
144       assertEquals(200, ks.get(0).getTimestamp());
145       assertEquals(ks.get(0), r.getColumnLatestCell(family, qf));
146     }
147   }
148 
149   public void testBasicGetValue() throws Exception {
150     KeyValue [] kvs = genKVs(row, family, value, 1, 100);
151 
152     Arrays.sort(kvs, KeyValue.COMPARATOR);
153 
154     Result r = Result.create(kvs);
155 
156     for (int i = 0; i < 100; ++i) {
157       final byte[] qf = Bytes.toBytes(i);
158 
159       assertByteEquals(Bytes.add(value, Bytes.toBytes(i)), r.getValue(family, qf));
160       assertTrue(r.containsColumn(family, qf));
161     }
162   }
163 
164   public void testMultiVersionGetValue() throws Exception {
165     KeyValue [] kvs1 = genKVs(row, family, value, 1, 100);
166     KeyValue [] kvs2 = genKVs(row, family, value, 200, 100);
167 
168     KeyValue [] kvs = new KeyValue[kvs1.length+kvs2.length];
169     System.arraycopy(kvs1, 0, kvs, 0, kvs1.length);
170     System.arraycopy(kvs2, 0, kvs, kvs1.length, kvs2.length);
171 
172     Arrays.sort(kvs, KeyValue.COMPARATOR);
173 
174     Result r = Result.create(kvs);
175     for (int i = 0; i < 100; ++i) {
176       final byte[] qf = Bytes.toBytes(i);
177 
178       assertByteEquals(Bytes.add(value, Bytes.toBytes(i)), r.getValue(family, qf));
179       assertTrue(r.containsColumn(family, qf));
180     }
181   }
182 
183   public void testBasicLoadValue() throws Exception {
184     KeyValue [] kvs = genKVs(row, family, value, 1, 100);
185 
186     Arrays.sort(kvs, KeyValue.COMPARATOR);
187 
188     Result r = Result.create(kvs);
189     ByteBuffer loadValueBuffer = ByteBuffer.allocate(1024);
190 
191     for (int i = 0; i < 100; ++i) {
192       final byte[] qf = Bytes.toBytes(i);
193 
194       loadValueBuffer.clear();
195       r.loadValue(family, qf, loadValueBuffer);
196       loadValueBuffer.flip();
197       assertEquals(ByteBuffer.wrap(Bytes.add(value, Bytes.toBytes(i))), loadValueBuffer);
198       assertEquals(ByteBuffer.wrap(Bytes.add(value, Bytes.toBytes(i))),
199           r.getValueAsByteBuffer(family, qf));
200     }
201   }
202 
203   public void testMultiVersionLoadValue() throws Exception {
204     KeyValue [] kvs1 = genKVs(row, family, value, 1, 100);
205     KeyValue [] kvs2 = genKVs(row, family, value, 200, 100);
206 
207     KeyValue [] kvs = new KeyValue[kvs1.length+kvs2.length];
208     System.arraycopy(kvs1, 0, kvs, 0, kvs1.length);
209     System.arraycopy(kvs2, 0, kvs, kvs1.length, kvs2.length);
210 
211     Arrays.sort(kvs, KeyValue.COMPARATOR);
212 
213     ByteBuffer loadValueBuffer = ByteBuffer.allocate(1024);
214 
215     Result r = Result.create(kvs);
216     for (int i = 0; i < 100; ++i) {
217       final byte[] qf = Bytes.toBytes(i);
218 
219       loadValueBuffer.clear();
220       r.loadValue(family, qf, loadValueBuffer);
221       loadValueBuffer.flip();
222       assertEquals(ByteBuffer.wrap(Bytes.add(value, Bytes.toBytes(i))), loadValueBuffer);
223       assertEquals(ByteBuffer.wrap(Bytes.add(value, Bytes.toBytes(i))),
224           r.getValueAsByteBuffer(family, qf));
225     }
226   }
227 
228   /**
229    * Verify that Result.compareResults(...) behaves correctly.
230    */
231   public void testCompareResults() throws Exception {
232     byte [] value1 = Bytes.toBytes("value1");
233     byte [] qual = Bytes.toBytes("qual");
234 
235     KeyValue kv1 = new KeyValue(row, family, qual, value);
236     KeyValue kv2 = new KeyValue(row, family, qual, value1);
237 
238     Result r1 = Result.create(new KeyValue[] {kv1});
239     Result r2 = Result.create(new KeyValue[] {kv2});
240     // no exception thrown
241     Result.compareResults(r1, r1);
242     try {
243       // these are different (HBASE-4800)
244       Result.compareResults(r1, r2);
245       fail();
246     } catch (Exception x) {
247       assertTrue(x.getMessage().startsWith("This result was different:"));
248     }
249   }
250 
251   /**
252    *  Test Result#compareResults when tags are present.
253    * @throws Exception Exception
254    */
255   @Test
256   public void testCompareResultsWithTags() throws Exception {
257     byte[] qualifier = Bytes.toBytes("q");
258     String tagValue1 = "tagV1";
259     String tagValue2 = "tagV2";
260     // Create cell with 2 tags
261     KeyValue kv1 = new KeyValue(row, family, qualifier, HConstants.LATEST_TIMESTAMP,
262       value, new Tag[] {new Tag((byte) 1, tagValue1), new Tag((byte) 2, tagValue2)});
263 
264     // Create cell with no tags.
265     KeyValue kv2 = new KeyValue(row, family, qualifier, HConstants.LATEST_TIMESTAMP,
266       value);
267 
268     Result r1 = Result.create(new KeyValue[] {kv1});
269     Result r2 = Result.create(new KeyValue[] {kv2});
270     // Compare result with self.
271     Result.compareResults(r1, r1);
272 
273     try {
274       // Compare result with 2 tags v/s no tags.
275       Result.compareResults(r1, r2);
276       fail();
277     } catch (Exception e) {
278       assertTrue(e.getMessage().startsWith("This result was different:"));
279     }
280 
281     // Create cell with just 1 tag.
282     String tagValue3 = "tagV3";
283     KeyValue kv3 = new KeyValue(row, family, qualifier, HConstants.LATEST_TIMESTAMP,
284       value, new Tag[] {new Tag((byte) 1, tagValue3)});
285     Result r3 = Result.create(new KeyValue[] {kv3});
286     try {
287       // Compare result with 2 tags v/s 1 tag.
288       Result.compareResults(r1, r3);
289       fail();
290     } catch (Exception e) {
291       assertTrue(e.getMessage().startsWith("This result was different:"));
292     }
293   }
294 
295   /**
296    * Verifies that one can't modify instance of EMPTY_RESULT.
297    */
298   public void testEmptyResultIsReadonly() {
299     Result emptyResult = Result.EMPTY_RESULT;
300     Result otherResult = new Result();
301 
302     try {
303       emptyResult.copyFrom(otherResult);
304       fail("UnsupportedOperationException should have been thrown!");
305     } catch (UnsupportedOperationException ex) {
306       LOG.debug("As expected: " + ex.getMessage());
307     }
308     try {
309       emptyResult.addResults(ClientProtos.RegionLoadStats.getDefaultInstance());
310       fail("UnsupportedOperationException should have been thrown!");
311     } catch (UnsupportedOperationException ex) {
312       LOG.debug("As expected: " + ex.getMessage());
313     }
314     try {
315       emptyResult.setExists(true);
316       fail("UnsupportedOperationException should have been thrown!");
317     } catch (UnsupportedOperationException ex) {
318       LOG.debug("As expected: " + ex.getMessage());
319     }
320   }
321 
322   /**
323    * Microbenchmark that compares {@link Result#getValue} and {@link Result#loadValue} performance.
324    *
325    * @throws Exception
326    */
327   public void doReadBenchmark() throws Exception {
328 
329     final int n = 5;
330     final int m = 100000000;
331 
332     StringBuilder valueSB = new StringBuilder();
333     for (int i = 0; i < 100; i++) {
334       valueSB.append((byte)(Math.random() * 10));
335     }
336 
337     StringBuilder rowSB = new StringBuilder();
338     for (int i = 0; i < 50; i++) {
339       rowSB.append((byte)(Math.random() * 10));
340     }
341 
342     KeyValue [] kvs = genKVs(Bytes.toBytes(rowSB.toString()), family,
343         Bytes.toBytes(valueSB.toString()), 1, n);
344     Arrays.sort(kvs, KeyValue.COMPARATOR);
345     ByteBuffer loadValueBuffer = ByteBuffer.allocate(1024);
346     Result r = Result.create(kvs);
347 
348     byte[][] qfs = new byte[n][Bytes.SIZEOF_INT];
349     for (int i = 0; i < n; ++i) {
350       System.arraycopy(qfs[i], 0, Bytes.toBytes(i), 0, Bytes.SIZEOF_INT);
351     }
352 
353     // warm up
354     for (int k = 0; k < 100000; k++) {
355       for (int i = 0; i < n; ++i) {
356         r.getValue(family, qfs[i]);
357         loadValueBuffer.clear();
358         r.loadValue(family, qfs[i], loadValueBuffer);
359         loadValueBuffer.flip();
360       }
361     }
362 
363     System.gc();
364     long start = System.nanoTime();
365     for (int k = 0; k < m; k++) {
366       for (int i = 0; i < n; ++i) {
367         loadValueBuffer.clear();
368         r.loadValue(family, qfs[i], loadValueBuffer);
369         loadValueBuffer.flip();
370       }
371     }
372     long stop = System.nanoTime();
373     System.out.println("loadValue(): " + (stop - start));
374 
375     System.gc();
376     start = System.nanoTime();
377     for (int k = 0; k < m; k++) {
378       for (int i = 0; i < n; i++) {
379         r.getValue(family, qfs[i]);
380       }
381     }
382     stop = System.nanoTime();
383     System.out.println("getValue():  " + (stop - start));
384   }
385 
386   /**
387    * Calls non-functional test methods.
388    *
389    * @param args
390    */
391   public static void main(String[] args) {
392     TestResult testResult = new TestResult();
393     try {
394       testResult.doReadBenchmark();
395     } catch (Exception e) {
396       LOG.error("Unexpected exception", e);
397     }
398   }
399 }