001/**
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.hbase.client;
019
020import static org.apache.hadoop.hbase.HBaseTestCase.assertByteEquals;
021
022import java.io.IOException;
023import java.nio.ByteBuffer;
024import java.util.Arrays;
025import java.util.List;
026import java.util.NoSuchElementException;
027import junit.framework.TestCase;
028import org.apache.hadoop.hbase.Cell;
029import org.apache.hadoop.hbase.CellComparator;
030import org.apache.hadoop.hbase.CellScanner;
031import org.apache.hadoop.hbase.CellUtil;
032import org.apache.hadoop.hbase.HBaseClassTestRule;
033import org.apache.hadoop.hbase.KeyValue;
034import org.apache.hadoop.hbase.testclassification.ClientTests;
035import org.apache.hadoop.hbase.testclassification.SmallTests;
036import org.apache.hadoop.hbase.util.Bytes;
037import org.junit.ClassRule;
038import org.junit.experimental.categories.Category;
039import org.slf4j.Logger;
040import org.slf4j.LoggerFactory;
041
042@Category({SmallTests.class, ClientTests.class})
043public class TestResult extends TestCase {
044
045  @ClassRule
046  public static final HBaseClassTestRule CLASS_RULE =
047      HBaseClassTestRule.forClass(TestResult.class);
048
049  private static final Logger LOG = LoggerFactory.getLogger(TestResult.class.getName());
050
051  static KeyValue[] genKVs(final byte[] row, final byte[] family,
052                           final byte[] value,
053                    final long timestamp,
054                    final int cols) {
055    KeyValue [] kvs = new KeyValue[cols];
056
057    for (int i = 0; i < cols ; i++) {
058      kvs[i] = new KeyValue(
059          row, family, Bytes.toBytes(i),
060          timestamp,
061          Bytes.add(value, Bytes.toBytes(i)));
062    }
063    return kvs;
064  }
065
066  static final byte [] row = Bytes.toBytes("row");
067  static final byte [] family = Bytes.toBytes("family");
068  static final byte [] value = Bytes.toBytes("value");
069
070  /**
071   * Run some tests to ensure Result acts like a proper CellScanner.
072   * @throws IOException
073   */
074  public void testResultAsCellScanner() throws IOException {
075    Cell [] cells = genKVs(row, family, value, 1, 10);
076    Arrays.sort(cells, CellComparator.getInstance());
077    Result r = Result.create(cells);
078    assertSame(r, cells);
079    // Assert I run over same result multiple times.
080    assertSame(r.cellScanner(), cells);
081    assertSame(r.cellScanner(), cells);
082    // Assert we are not creating new object when doing cellscanner
083    assertTrue(r == r.cellScanner());
084  }
085
086  private void assertSame(final CellScanner cellScanner, final Cell [] cells) throws IOException {
087    int count = 0;
088    while (cellScanner.advance()) {
089      assertTrue(cells[count].equals(cellScanner.current()));
090      count++;
091    }
092    assertEquals(cells.length, count);
093  }
094
095  public void testBasicGetColumn() throws Exception {
096    KeyValue [] kvs = genKVs(row, family, value, 1, 100);
097
098    Arrays.sort(kvs, CellComparator.getInstance());
099
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, CellComparator.getInstance());
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, CellComparator.getInstance());
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, CellComparator.getInstance());
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, CellComparator.getInstance());
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(loadValueBuffer, ByteBuffer.wrap(Bytes.add(value, Bytes.toBytes(i))));
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, CellComparator.getInstance());
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(loadValueBuffer, ByteBuffer.wrap(Bytes.add(value, Bytes.toBytes(i))));
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   * Verifies that one can't modify instance of EMPTY_RESULT.
255   */
256  public void testEmptyResultIsReadonly() {
257    Result emptyResult = Result.EMPTY_RESULT;
258    Result otherResult = new Result();
259
260    try {
261      emptyResult.copyFrom(otherResult);
262      fail("UnsupportedOperationException should have been thrown!");
263    } catch (UnsupportedOperationException ex) {
264      LOG.debug("As expected: " + ex.getMessage());
265    }
266    try {
267      emptyResult.setExists(true);
268      fail("UnsupportedOperationException should have been thrown!");
269    } catch (UnsupportedOperationException ex) {
270      LOG.debug("As expected: " + ex.getMessage());
271    }
272  }
273
274  /**
275   * Microbenchmark that compares {@link Result#getValue} and {@link Result#loadValue} performance.
276   *
277   * @throws Exception
278   */
279  public void doReadBenchmark() throws Exception {
280
281    final int n = 5;
282    final int m = 100000000;
283
284    StringBuilder valueSB = new StringBuilder();
285    for (int i = 0; i < 100; i++) {
286      valueSB.append((byte)(Math.random() * 10));
287    }
288
289    StringBuilder rowSB = new StringBuilder();
290    for (int i = 0; i < 50; i++) {
291      rowSB.append((byte)(Math.random() * 10));
292    }
293
294    KeyValue [] kvs = genKVs(Bytes.toBytes(rowSB.toString()), family,
295        Bytes.toBytes(valueSB.toString()), 1, n);
296    Arrays.sort(kvs, CellComparator.getInstance());
297    ByteBuffer loadValueBuffer = ByteBuffer.allocate(1024);
298    Result r = Result.create(kvs);
299
300    byte[][] qfs = new byte[n][Bytes.SIZEOF_INT];
301    for (int i = 0; i < n; ++i) {
302      System.arraycopy(qfs[i], 0, Bytes.toBytes(i), 0, Bytes.SIZEOF_INT);
303    }
304
305    // warm up
306    for (int k = 0; k < 100000; k++) {
307      for (int i = 0; i < n; ++i) {
308        r.getValue(family, qfs[i]);
309        loadValueBuffer.clear();
310        r.loadValue(family, qfs[i], loadValueBuffer);
311        loadValueBuffer.flip();
312      }
313    }
314
315    System.gc();
316    long start = System.nanoTime();
317    for (int k = 0; k < m; k++) {
318      for (int i = 0; i < n; ++i) {
319        loadValueBuffer.clear();
320        r.loadValue(family, qfs[i], loadValueBuffer);
321        loadValueBuffer.flip();
322      }
323    }
324    long stop = System.nanoTime();
325    System.out.println("loadValue(): " + (stop - start));
326
327    System.gc();
328    start = System.nanoTime();
329    for (int k = 0; k < m; k++) {
330      for (int i = 0; i < n; i++) {
331        r.getValue(family, qfs[i]);
332      }
333    }
334    stop = System.nanoTime();
335    System.out.println("getValue():  " + (stop - start));
336  }
337
338  /**
339   * Calls non-functional test methods.
340   *
341   * @param args
342   */
343  public static void main(String[] args) {
344    TestResult testResult = new TestResult();
345    try {
346      testResult.doReadBenchmark();
347    } catch (Exception e) {
348      LOG.error("Unexpected exception", e);
349    }
350  }
351}