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}