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.hamcrest.MatcherAssert.assertThat; 021import static org.hamcrest.Matchers.*; 022import static org.hamcrest.Matchers.lessThan; 023import static org.junit.jupiter.api.Assertions.assertArrayEquals; 024import static org.junit.jupiter.api.Assertions.assertEquals; 025import static org.junit.jupiter.api.Assertions.assertFalse; 026import static org.junit.jupiter.api.Assertions.assertNotSame; 027import static org.junit.jupiter.api.Assertions.assertNull; 028import static org.junit.jupiter.api.Assertions.assertSame; 029import static org.junit.jupiter.api.Assertions.assertThrows; 030import static org.junit.jupiter.api.Assertions.assertTrue; 031import static org.junit.jupiter.api.Assertions.fail; 032 033import java.io.IOException; 034import java.nio.ByteBuffer; 035import java.util.ArrayList; 036import java.util.Arrays; 037import java.util.List; 038import org.apache.hadoop.hbase.ArrayBackedTag; 039import org.apache.hadoop.hbase.ByteBufferKeyValue; 040import org.apache.hadoop.hbase.Cell; 041import org.apache.hadoop.hbase.CellComparator; 042import org.apache.hadoop.hbase.CellScanner; 043import org.apache.hadoop.hbase.CellUtil; 044import org.apache.hadoop.hbase.KeyValue; 045import org.apache.hadoop.hbase.Tag; 046import org.apache.hadoop.hbase.testclassification.ClientTests; 047import org.apache.hadoop.hbase.testclassification.SmallTests; 048import org.apache.hadoop.hbase.util.ByteBufferUtils; 049import org.apache.hadoop.hbase.util.Bytes; 050import org.apache.hadoop.hbase.util.EnvironmentEdgeManager; 051import org.junit.jupiter.api.Test; 052import org.slf4j.Logger; 053import org.slf4j.LoggerFactory; 054 055@org.junit.jupiter.api.Tag(SmallTests.TAG) 056@org.junit.jupiter.api.Tag(ClientTests.TAG) 057public class TestResult { 058 059 private static final Logger LOG = LoggerFactory.getLogger(TestResult.class.getName()); 060 061 static KeyValue[] genKVs(final byte[] row, final byte[] family, final byte[] value, 062 final long timestamp, final int cols) { 063 KeyValue[] kvs = new KeyValue[cols]; 064 065 for (int i = 0; i < cols; i++) { 066 kvs[i] = 067 new KeyValue(row, family, Bytes.toBytes(i), timestamp, Bytes.add(value, Bytes.toBytes(i))); 068 } 069 return kvs; 070 } 071 072 static final byte[] row = Bytes.toBytes("row"); 073 static final byte[] family = Bytes.toBytes("family"); 074 static final byte[] value = Bytes.toBytes("value"); 075 static final byte[] qual = Bytes.toBytes("qual"); 076 077 /** 078 * Run some tests to ensure Result acts like a proper CellScanner. 079 */ 080 @Test 081 public void testResultAsCellScanner() throws IOException { 082 Cell[] cells = genKVs(row, family, value, 1, 10); 083 Arrays.sort(cells, CellComparator.getInstance()); 084 Result r = Result.create(cells); 085 assertCellsSame(r, cells); 086 // Assert I run over same result multiple times. 087 assertCellsSame(r.cellScanner(), cells); 088 assertCellsSame(r.cellScanner(), cells); 089 // Assert we are not creating new object when doing cellscanner 090 assertSame(r, r.cellScanner()); 091 } 092 093 private void assertCellsSame(final CellScanner cellScanner, final Cell[] cells) 094 throws IOException { 095 int count = 0; 096 while (cellScanner.advance()) { 097 assertTrue(cells[count].equals(cellScanner.current())); 098 count++; 099 } 100 assertEquals(cells.length, count); 101 } 102 103 @Test 104 public void testBasicGetColumn() throws Exception { 105 KeyValue[] kvs = genKVs(row, family, value, 1, 100); 106 107 Arrays.sort(kvs, CellComparator.getInstance()); 108 109 Result r = Result.create(kvs); 110 111 for (int i = 0; i < 100; ++i) { 112 final byte[] qf = Bytes.toBytes(i); 113 114 List<Cell> ks = r.getColumnCells(family, qf); 115 assertEquals(1, ks.size()); 116 assertTrue(CellUtil.matchingQualifier(ks.get(0), qf)); 117 assertEquals(ks.get(0), r.getColumnLatestCell(family, qf)); 118 } 119 } 120 121 @Test 122 public void testCurrentOnEmptyCell() throws IOException { 123 Result r = Result.create(new Cell[0]); 124 assertFalse(r.advance()); 125 assertNull(r.current()); 126 } 127 128 @Test 129 public void testAdvanceMultipleOnEmptyCell() throws IOException { 130 Result r = Result.create(new Cell[0]); 131 // After HBASE-26688, advance of result with empty cell list will always return false. 132 // Here 10 is an arbitrary number to test the logic. 133 for (int i = 0; i < 10; i++) { 134 assertFalse(r.advance()); 135 } 136 } 137 138 @Test 139 public void testMultiVersionGetColumn() throws Exception { 140 KeyValue[] kvs1 = genKVs(row, family, value, 1, 100); 141 KeyValue[] kvs2 = genKVs(row, family, value, 200, 100); 142 143 KeyValue[] kvs = new KeyValue[kvs1.length + kvs2.length]; 144 System.arraycopy(kvs1, 0, kvs, 0, kvs1.length); 145 System.arraycopy(kvs2, 0, kvs, kvs1.length, kvs2.length); 146 147 Arrays.sort(kvs, CellComparator.getInstance()); 148 149 Result r = Result.create(kvs); 150 for (int i = 0; i < 100; ++i) { 151 final byte[] qf = Bytes.toBytes(i); 152 153 List<Cell> ks = r.getColumnCells(family, qf); 154 assertEquals(2, ks.size()); 155 assertTrue(CellUtil.matchingQualifier(ks.get(0), qf)); 156 assertEquals(200, ks.get(0).getTimestamp()); 157 assertEquals(ks.get(0), r.getColumnLatestCell(family, qf)); 158 } 159 } 160 161 @Test 162 public void testBasicGetValue() throws Exception { 163 KeyValue[] kvs = genKVs(row, family, value, 1, 100); 164 165 Arrays.sort(kvs, CellComparator.getInstance()); 166 167 Result r = Result.create(kvs); 168 169 for (int i = 0; i < 100; ++i) { 170 final byte[] qf = Bytes.toBytes(i); 171 172 assertArrayEquals(Bytes.add(value, Bytes.toBytes(i)), r.getValue(family, qf)); 173 assertTrue(r.containsColumn(family, qf)); 174 } 175 } 176 177 @Test 178 public void testMultiVersionGetValue() throws Exception { 179 KeyValue[] kvs1 = genKVs(row, family, value, 1, 100); 180 KeyValue[] kvs2 = genKVs(row, family, value, 200, 100); 181 182 KeyValue[] kvs = new KeyValue[kvs1.length + kvs2.length]; 183 System.arraycopy(kvs1, 0, kvs, 0, kvs1.length); 184 System.arraycopy(kvs2, 0, kvs, kvs1.length, kvs2.length); 185 186 Arrays.sort(kvs, CellComparator.getInstance()); 187 188 Result r = Result.create(kvs); 189 for (int i = 0; i < 100; ++i) { 190 final byte[] qf = Bytes.toBytes(i); 191 192 assertArrayEquals(Bytes.add(value, Bytes.toBytes(i)), r.getValue(family, qf)); 193 assertTrue(r.containsColumn(family, qf)); 194 } 195 } 196 197 @Test 198 public void testBasicLoadValue() throws Exception { 199 KeyValue[] kvs = genKVs(row, family, value, 1, 100); 200 201 Arrays.sort(kvs, CellComparator.getInstance()); 202 203 Result r = Result.create(kvs); 204 ByteBuffer loadValueBuffer = ByteBuffer.allocate(1024); 205 206 for (int i = 0; i < 100; ++i) { 207 final byte[] qf = Bytes.toBytes(i); 208 209 loadValueBuffer.clear(); 210 r.loadValue(family, qf, loadValueBuffer); 211 loadValueBuffer.flip(); 212 assertEquals(loadValueBuffer, ByteBuffer.wrap(Bytes.add(value, Bytes.toBytes(i)))); 213 assertEquals(ByteBuffer.wrap(Bytes.add(value, Bytes.toBytes(i))), 214 r.getValueAsByteBuffer(family, qf)); 215 } 216 } 217 218 @Test 219 public void testMultiVersionLoadValue() throws Exception { 220 KeyValue[] kvs1 = genKVs(row, family, value, 1, 100); 221 KeyValue[] kvs2 = genKVs(row, family, value, 200, 100); 222 223 KeyValue[] kvs = new KeyValue[kvs1.length + kvs2.length]; 224 System.arraycopy(kvs1, 0, kvs, 0, kvs1.length); 225 System.arraycopy(kvs2, 0, kvs, kvs1.length, kvs2.length); 226 227 Arrays.sort(kvs, CellComparator.getInstance()); 228 229 ByteBuffer loadValueBuffer = ByteBuffer.allocate(1024); 230 231 Result r = Result.create(kvs); 232 for (int i = 0; i < 100; ++i) { 233 final byte[] qf = Bytes.toBytes(i); 234 235 loadValueBuffer.clear(); 236 r.loadValue(family, qf, loadValueBuffer); 237 loadValueBuffer.flip(); 238 assertEquals(loadValueBuffer, ByteBuffer.wrap(Bytes.add(value, Bytes.toBytes(i)))); 239 assertEquals(ByteBuffer.wrap(Bytes.add(value, Bytes.toBytes(i))), 240 r.getValueAsByteBuffer(family, qf)); 241 } 242 } 243 244 /** 245 * Verify that Result.compareResults(...) behaves correctly. 246 */ 247 @Test 248 public void testCompareResults() throws Exception { 249 byte[] value1 = Bytes.toBytes("value1"); 250 byte[] qual = Bytes.toBytes("qual"); 251 252 KeyValue kv1 = new KeyValue(row, family, qual, value); 253 KeyValue kv2 = new KeyValue(row, family, qual, value1); 254 255 Result r1 = Result.create(new KeyValue[] { kv1 }); 256 Result r2 = Result.create(new KeyValue[] { kv2 }); 257 // no exception thrown 258 Result.compareResults(r1, r1); 259 // these are different (HBASE-4800) 260 Exception x = assertThrows(Exception.class, () -> Result.compareResults(r1, r2)); 261 assertThat(x.getMessage(), startsWith("This result was different:")); 262 } 263 264 @Test 265 public void testCompareResultsWithTags() throws Exception { 266 Tag t1 = new ArrayBackedTag((byte) 1, Bytes.toBytes("TAG1")); 267 Tag t2 = new ArrayBackedTag((byte) 2, Bytes.toBytes("TAG2")); 268 // Both BB backed tags KV are null 269 Result result1 = getByteBufferBackedTagResult(null); 270 Result result2 = getByteBufferBackedTagResult(null); 271 Result.compareResults(result1, result2); 272 273 // Test both byte buffer backed tags KeyValue 274 result1 = getByteBufferBackedTagResult(t1); 275 result2 = getByteBufferBackedTagResult(t1); 276 Result.compareResults(result1, result2); 277 278 // Both array backed tags KV are null 279 result1 = getArrayBackedTagResult(null); 280 result2 = getArrayBackedTagResult(null); 281 Result.compareResults(result1, result2); 282 283 // Test both array backed tags KeyValue 284 result1 = getArrayBackedTagResult(t1); 285 result2 = getArrayBackedTagResult(t1); 286 Result.compareResults(result1, result2); 287 288 // left instance of byte buffer and right instance of array backed 289 result1 = getByteBufferBackedTagResult(t1); 290 result2 = getArrayBackedTagResult(t1); 291 Result.compareResults(result1, result2); 292 293 // left instance of array backed and right instance of byte buffer backed. 294 result1 = getArrayBackedTagResult(t1); 295 result2 = getByteBufferBackedTagResult(t1); 296 Result.compareResults(result1, result2); 297 298 // Left BB backed null tag and right BB backed non null tag 299 result1 = getByteBufferBackedTagResult(null); 300 result2 = getByteBufferBackedTagResult(t2); 301 try { 302 Result.compareResults(result1, result2); 303 fail(); 304 } catch (Exception e) { 305 // Expected 306 } 307 308 // Left BB backed non null tag and right BB backed null tag 309 result1 = getByteBufferBackedTagResult(t1); 310 result2 = getByteBufferBackedTagResult(null); 311 try { 312 Result.compareResults(result1, result2); 313 fail(); 314 } catch (Exception e) { 315 // Expected 316 } 317 318 // Both byte buffer backed tags KV are different 319 result1 = getByteBufferBackedTagResult(t1); 320 result2 = getByteBufferBackedTagResult(t2); 321 try { 322 Result.compareResults(result1, result2); 323 fail(); 324 } catch (Exception e) { 325 // Expected 326 } 327 328 // Left array backed non null tag and right array backed null tag 329 result1 = getArrayBackedTagResult(t1); 330 result2 = getArrayBackedTagResult(null); 331 try { 332 Result.compareResults(result1, result2); 333 fail(); 334 } catch (Exception e) { 335 // Expected 336 } 337 338 // Left array backed null tag and right array backed non null tag 339 result1 = getByteBufferBackedTagResult(null); 340 result2 = getByteBufferBackedTagResult(t2); 341 try { 342 Result.compareResults(result1, result2); 343 fail(); 344 } catch (Exception e) { 345 // Expected 346 } 347 348 // Both array backed tags KV are different 349 result1 = getArrayBackedTagResult(t1); 350 result2 = getArrayBackedTagResult(t2); 351 try { 352 Result.compareResults(result1, result2); 353 fail(); 354 } catch (Exception e) { 355 // Expected 356 } 357 358 // left instance of byte buffer and right instance of array backed are different 359 result1 = getByteBufferBackedTagResult(t1); 360 result2 = getArrayBackedTagResult(t2); 361 try { 362 Result.compareResults(result1, result2); 363 fail(); 364 } catch (Exception e) { 365 // Expected 366 } 367 368 // left instance of array backed and right instance of byte buffer backed are different 369 result1 = getArrayBackedTagResult(t1); 370 result2 = getByteBufferBackedTagResult(t2); 371 try { 372 Result.compareResults(result1, result2); 373 fail(); 374 } catch (Exception e) { 375 // Expected 376 } 377 } 378 379 @Test 380 public void testCompareResultMemoryUsage() { 381 List<Cell> cells1 = new ArrayList<>(); 382 for (long i = 0; i < 100; i++) { 383 cells1.add(new KeyValue(row, family, Bytes.toBytes(i), value)); 384 } 385 386 List<Cell> cells2 = new ArrayList<>(); 387 for (long i = 0; i < 100; i++) { 388 cells2.add(new KeyValue(row, family, Bytes.toBytes(i), Bytes.toBytes(i))); 389 } 390 391 Result r1 = Result.create(cells1); 392 Result r2 = Result.create(cells2); 393 try { 394 Result.compareResults(r1, r2); 395 fail(); 396 } catch (Exception x) { 397 assertTrue(x.getMessage().startsWith("This result was different:")); 398 assertThat(x.getMessage().length(), greaterThan(100)); 399 } 400 401 try { 402 Result.compareResults(r1, r2, false); 403 fail(); 404 } catch (Exception x) { 405 assertEquals("This result was different: row=row", x.getMessage()); 406 assertThat(x.getMessage().length(), lessThan(100)); 407 } 408 } 409 410 private Result getArrayBackedTagResult(Tag tag) { 411 List<Tag> tags = null; 412 if (tag != null) { 413 tags = Arrays.asList(tag); 414 } 415 KeyValue kvCell = new KeyValue(row, family, qual, 0L, KeyValue.Type.Put, value, tags); 416 return Result.create(new Cell[] { kvCell }); 417 } 418 419 private Result getByteBufferBackedTagResult(Tag tag) { 420 List<Tag> tags = null; 421 if (tag != null) { 422 tags = Arrays.asList(tag); 423 } 424 KeyValue kvCell = new KeyValue(row, family, qual, 0L, KeyValue.Type.Put, value, tags); 425 ByteBuffer buf = ByteBuffer.allocateDirect(kvCell.getBuffer().length); 426 ByteBufferUtils.copyFromArrayToBuffer(buf, kvCell.getBuffer(), 0, kvCell.getBuffer().length); 427 ByteBufferKeyValue bbKV = new ByteBufferKeyValue(buf, 0, buf.capacity(), 0L); 428 return Result.create(new Cell[] { bbKV }); 429 } 430 431 /** 432 * Verifies that one can't modify instance of EMPTY_RESULT. 433 */ 434 @Test 435 public void testEmptyResultIsReadonly() { 436 Result emptyResult = Result.EMPTY_RESULT; 437 Result otherResult = new Result(); 438 439 try { 440 emptyResult.copyFrom(otherResult); 441 fail("UnsupportedOperationException should have been thrown!"); 442 } catch (UnsupportedOperationException ex) { 443 LOG.debug("As expected: " + ex.getMessage()); 444 } 445 try { 446 emptyResult.setExists(true); 447 fail("UnsupportedOperationException should have been thrown!"); 448 } catch (UnsupportedOperationException ex) { 449 LOG.debug("As expected: " + ex.getMessage()); 450 } 451 } 452 453 /** 454 * Microbenchmark that compares {@link Result#getValue} and {@link Result#loadValue} performance. 455 */ 456 public void doReadBenchmark() throws Exception { 457 final int n = 5; 458 final int m = 100000000; 459 460 StringBuilder valueSB = new StringBuilder(); 461 for (int i = 0; i < 100; i++) { 462 valueSB.append((byte) (Math.random() * 10)); 463 } 464 465 StringBuilder rowSB = new StringBuilder(); 466 for (int i = 0; i < 50; i++) { 467 rowSB.append((byte) (Math.random() * 10)); 468 } 469 470 KeyValue[] kvs = 471 genKVs(Bytes.toBytes(rowSB.toString()), family, Bytes.toBytes(valueSB.toString()), 1, n); 472 Arrays.sort(kvs, CellComparator.getInstance()); 473 ByteBuffer loadValueBuffer = ByteBuffer.allocate(1024); 474 Result r = Result.create(kvs); 475 476 byte[][] qfs = new byte[n][Bytes.SIZEOF_INT]; 477 for (int i = 0; i < n; ++i) { 478 System.arraycopy(qfs[i], 0, Bytes.toBytes(i), 0, Bytes.SIZEOF_INT); 479 } 480 481 // warm up 482 for (int k = 0; k < 100000; k++) { 483 for (int i = 0; i < n; ++i) { 484 r.getValue(family, qfs[i]); 485 loadValueBuffer.clear(); 486 r.loadValue(family, qfs[i], loadValueBuffer); 487 loadValueBuffer.flip(); 488 } 489 } 490 491 System.gc(); 492 long start = System.nanoTime(); 493 for (int k = 0; k < m; k++) { 494 for (int i = 0; i < n; ++i) { 495 loadValueBuffer.clear(); 496 r.loadValue(family, qfs[i], loadValueBuffer); 497 loadValueBuffer.flip(); 498 } 499 } 500 long stop = System.nanoTime(); 501 LOG.info("loadValue(): " + (stop - start)); 502 503 System.gc(); 504 start = System.nanoTime(); 505 for (int k = 0; k < m; k++) { 506 for (int i = 0; i < n; i++) { 507 r.getValue(family, qfs[i]); 508 } 509 } 510 stop = System.nanoTime(); 511 LOG.info("getValue(): " + (stop - start)); 512 } 513 514 @Test 515 public void testCreateResultWithCellArray() { 516 Cell[] cells = genKVs(row, family, value, EnvironmentEdgeManager.currentTime(), 5); 517 Result r = Result.create(cells); 518 // the cells is actually a KeyValue[], which can be cast to ExtendedCell[] directly, so we 519 // should get the same one without copying 520 assertSame(cells, r.rawCells()); 521 522 Cell[] emptyCells = new Cell[0]; 523 Result emptyResult = Result.create(emptyCells); 524 // emptyCells is a Cell[] instead of ExtendedCell[], so we need to copy it to a new array 525 assertNotSame(emptyCells, emptyResult.rawCells()); 526 } 527 528 /** 529 * Calls non-functional test methods. 530 */ 531 public static void main(String[] args) { 532 TestResult testResult = new TestResult(); 533 try { 534 testResult.doReadBenchmark(); 535 } catch (Exception e) { 536 LOG.error("Unexpected exception", e); 537 } 538 } 539}