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.HConstants.RPC_CODEC_CONF_KEY; 021import static org.apache.hadoop.hbase.client.TestFromClientSide3.generateHugeValue; 022import static org.apache.hadoop.hbase.ipc.RpcClient.DEFAULT_CODEC_CLASS; 023import static org.junit.Assert.assertArrayEquals; 024import static org.junit.Assert.assertEquals; 025import static org.junit.Assert.assertFalse; 026import static org.junit.Assert.assertNotNull; 027import static org.junit.Assert.assertNull; 028import static org.junit.Assert.assertTrue; 029import static org.junit.Assert.fail; 030 031import java.io.IOException; 032import java.util.ArrayList; 033import java.util.List; 034import java.util.concurrent.TimeUnit; 035import java.util.function.Consumer; 036import java.util.stream.IntStream; 037import org.apache.hadoop.conf.Configuration; 038import org.apache.hadoop.hbase.Cell; 039import org.apache.hadoop.hbase.CompareOperator; 040import org.apache.hadoop.hbase.HBaseClassTestRule; 041import org.apache.hadoop.hbase.HBaseTestingUtility; 042import org.apache.hadoop.hbase.HColumnDescriptor; 043import org.apache.hadoop.hbase.HConstants; 044import org.apache.hadoop.hbase.HRegionInfo; 045import org.apache.hadoop.hbase.HRegionLocation; 046import org.apache.hadoop.hbase.HTestConst; 047import org.apache.hadoop.hbase.KeyValue; 048import org.apache.hadoop.hbase.MiniHBaseCluster; 049import org.apache.hadoop.hbase.TableName; 050import org.apache.hadoop.hbase.TableNotFoundException; 051import org.apache.hadoop.hbase.filter.BinaryComparator; 052import org.apache.hadoop.hbase.filter.ColumnPrefixFilter; 053import org.apache.hadoop.hbase.filter.ColumnRangeFilter; 054import org.apache.hadoop.hbase.filter.QualifierFilter; 055import org.apache.hadoop.hbase.regionserver.HRegionServer; 056import org.apache.hadoop.hbase.testclassification.ClientTests; 057import org.apache.hadoop.hbase.testclassification.MediumTests; 058import org.apache.hadoop.hbase.util.Bytes; 059import org.apache.hadoop.hbase.util.EnvironmentEdgeManager; 060import org.junit.After; 061import org.junit.AfterClass; 062import org.junit.Before; 063import org.junit.BeforeClass; 064import org.junit.ClassRule; 065import org.junit.Rule; 066import org.junit.Test; 067import org.junit.experimental.categories.Category; 068import org.junit.rules.TestName; 069import org.slf4j.Logger; 070import org.slf4j.LoggerFactory; 071 072/** 073 * A client-side test, mostly testing scanners with various parameters. 074 */ 075@Category({MediumTests.class, ClientTests.class}) 076public class TestScannersFromClientSide { 077 078 @ClassRule 079 public static final HBaseClassTestRule CLASS_RULE = 080 HBaseClassTestRule.forClass(TestScannersFromClientSide.class); 081 082 private static final Logger LOG = LoggerFactory.getLogger(TestScannersFromClientSide.class); 083 084 private final static HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility(); 085 private static byte [] ROW = Bytes.toBytes("testRow"); 086 private static byte [] FAMILY = Bytes.toBytes("testFamily"); 087 private static byte [] QUALIFIER = Bytes.toBytes("testQualifier"); 088 private static byte [] VALUE = Bytes.toBytes("testValue"); 089 090 @Rule 091 public TestName name = new TestName(); 092 093 /** 094 * @throws java.lang.Exception 095 */ 096 @BeforeClass 097 public static void setUpBeforeClass() throws Exception { 098 Configuration conf = TEST_UTIL.getConfiguration(); 099 conf.setLong(HConstants.HBASE_CLIENT_SCANNER_MAX_RESULT_SIZE_KEY, 10 * 1024 * 1024); 100 TEST_UTIL.startMiniCluster(3); 101 } 102 103 /** 104 * @throws java.lang.Exception 105 */ 106 @AfterClass 107 public static void tearDownAfterClass() throws Exception { 108 TEST_UTIL.shutdownMiniCluster(); 109 } 110 111 /** 112 * @throws java.lang.Exception 113 */ 114 @Before 115 public void setUp() throws Exception { 116 // Nothing to do. 117 } 118 119 /** 120 * @throws java.lang.Exception 121 */ 122 @After 123 public void tearDown() throws Exception { 124 // Nothing to do. 125 } 126 127 /** 128 * Test from client side for batch of scan 129 * 130 * @throws Exception 131 */ 132 @Test 133 public void testScanBatch() throws Exception { 134 final TableName tableName = TableName.valueOf(name.getMethodName()); 135 byte [][] QUALIFIERS = HTestConst.makeNAscii(QUALIFIER, 8); 136 137 Table ht = TEST_UTIL.createTable(tableName, FAMILY); 138 139 Put put; 140 Scan scan; 141 Delete delete; 142 Result result; 143 ResultScanner scanner; 144 boolean toLog = true; 145 List<Cell> kvListExp; 146 147 // table: row, family, c0:0, c1:1, ... , c7:7 148 put = new Put(ROW); 149 for (int i=0; i < QUALIFIERS.length; i++) { 150 KeyValue kv = new KeyValue(ROW, FAMILY, QUALIFIERS[i], i, VALUE); 151 put.add(kv); 152 } 153 ht.put(put); 154 155 // table: row, family, c0:0, c1:1, ..., c6:2, c6:6 , c7:7 156 put = new Put(ROW); 157 KeyValue kv = new KeyValue(ROW, FAMILY, QUALIFIERS[6], 2, VALUE); 158 put.add(kv); 159 ht.put(put); 160 161 // delete upto ts: 3 162 delete = new Delete(ROW); 163 delete.addFamily(FAMILY, 3); 164 ht.delete(delete); 165 166 // without batch 167 scan = new Scan().withStartRow(ROW); 168 scan.setMaxVersions(); 169 scanner = ht.getScanner(scan); 170 171 // c4:4, c5:5, c6:6, c7:7 172 kvListExp = new ArrayList<>(); 173 kvListExp.add(new KeyValue(ROW, FAMILY, QUALIFIERS[4], 4, VALUE)); 174 kvListExp.add(new KeyValue(ROW, FAMILY, QUALIFIERS[5], 5, VALUE)); 175 kvListExp.add(new KeyValue(ROW, FAMILY, QUALIFIERS[6], 6, VALUE)); 176 kvListExp.add(new KeyValue(ROW, FAMILY, QUALIFIERS[7], 7, VALUE)); 177 result = scanner.next(); 178 verifyResult(result, kvListExp, toLog, "Testing first batch of scan"); 179 180 // with batch 181 scan = new Scan().withStartRow(ROW); 182 scan.setMaxVersions(); 183 scan.setBatch(2); 184 scanner = ht.getScanner(scan); 185 186 // First batch: c4:4, c5:5 187 kvListExp = new ArrayList<>(); 188 kvListExp.add(new KeyValue(ROW, FAMILY, QUALIFIERS[4], 4, VALUE)); 189 kvListExp.add(new KeyValue(ROW, FAMILY, QUALIFIERS[5], 5, VALUE)); 190 result = scanner.next(); 191 verifyResult(result, kvListExp, toLog, "Testing first batch of scan"); 192 193 // Second batch: c6:6, c7:7 194 kvListExp = new ArrayList<>(); 195 kvListExp.add(new KeyValue(ROW, FAMILY, QUALIFIERS[6], 6, VALUE)); 196 kvListExp.add(new KeyValue(ROW, FAMILY, QUALIFIERS[7], 7, VALUE)); 197 result = scanner.next(); 198 verifyResult(result, kvListExp, toLog, "Testing second batch of scan"); 199 200 } 201 202 @Test 203 public void testMaxResultSizeIsSetToDefault() throws Exception { 204 final TableName tableName = TableName.valueOf(name.getMethodName()); 205 Table ht = TEST_UTIL.createTable(tableName, FAMILY); 206 207 // The max result size we expect the scan to use by default. 208 long expectedMaxResultSize = 209 TEST_UTIL.getConfiguration().getLong(HConstants.HBASE_CLIENT_SCANNER_MAX_RESULT_SIZE_KEY, 210 HConstants.DEFAULT_HBASE_CLIENT_SCANNER_MAX_RESULT_SIZE); 211 212 int numRows = 5; 213 byte[][] ROWS = HTestConst.makeNAscii(ROW, numRows); 214 215 int numQualifiers = 10; 216 byte[][] QUALIFIERS = HTestConst.makeNAscii(QUALIFIER, numQualifiers); 217 218 // Specify the cell size such that a single row will be larger than the default 219 // value of maxResultSize. This means that Scan RPCs should return at most a single 220 // result back to the client. 221 int cellSize = (int) (expectedMaxResultSize / (numQualifiers - 1)); 222 byte[] cellValue = Bytes.createMaxByteArray(cellSize); 223 224 Put put; 225 List<Put> puts = new ArrayList<>(); 226 for (int row = 0; row < ROWS.length; row++) { 227 put = new Put(ROWS[row]); 228 for (int qual = 0; qual < QUALIFIERS.length; qual++) { 229 KeyValue kv = new KeyValue(ROWS[row], FAMILY, QUALIFIERS[qual], cellValue); 230 put.add(kv); 231 } 232 puts.add(put); 233 } 234 ht.put(puts); 235 236 // Create a scan with the default configuration. 237 Scan scan = new Scan(); 238 239 ResultScanner scanner = ht.getScanner(scan); 240 assertTrue(scanner instanceof ClientScanner); 241 ClientScanner clientScanner = (ClientScanner) scanner; 242 243 // Call next to issue a single RPC to the server 244 scanner.next(); 245 246 // The scanner should have, at most, a single result in its cache. If there more results exists 247 // in the cache it means that more than the expected max result size was fetched. 248 assertTrue("The cache contains: " + clientScanner.getCacheSize() + " results", 249 clientScanner.getCacheSize() <= 1); 250 } 251 252 /** 253 * Scan on not existing table should throw the exception with correct message 254 */ 255 @Test 256 public void testScannerForNotExistingTable() { 257 String[] tableNames = {"A", "Z", "A:A", "Z:Z"}; 258 for(String tableName : tableNames) { 259 try { 260 Table table = TEST_UTIL.getConnection().getTable(TableName.valueOf(tableName)); 261 testSmallScan(table, true, 1, 5); 262 fail("TableNotFoundException was not thrown"); 263 } catch (TableNotFoundException e) { 264 // We expect that the message for TableNotFoundException would have only the table name only 265 // Otherwise that would mean that localeRegionInMeta doesn't work properly 266 assertEquals(e.getMessage(), tableName); 267 } catch (Exception e) { 268 fail("Unexpected exception " + e.getMessage()); 269 } 270 } 271 } 272 273 @Test 274 public void testSmallScan() throws Exception { 275 final TableName tableName = TableName.valueOf(name.getMethodName()); 276 277 int numRows = 10; 278 byte[][] ROWS = HTestConst.makeNAscii(ROW, numRows); 279 280 int numQualifiers = 10; 281 byte[][] QUALIFIERS = HTestConst.makeNAscii(QUALIFIER, numQualifiers); 282 283 Table ht = TEST_UTIL.createTable(tableName, FAMILY); 284 285 Put put; 286 List<Put> puts = new ArrayList<>(); 287 for (int row = 0; row < ROWS.length; row++) { 288 put = new Put(ROWS[row]); 289 for (int qual = 0; qual < QUALIFIERS.length; qual++) { 290 KeyValue kv = new KeyValue(ROWS[row], FAMILY, QUALIFIERS[qual], VALUE); 291 put.add(kv); 292 } 293 puts.add(put); 294 } 295 ht.put(puts); 296 297 int expectedRows = numRows; 298 int expectedCols = numRows * numQualifiers; 299 300 // Test normal and reversed 301 testSmallScan(ht, true, expectedRows, expectedCols); 302 testSmallScan(ht, false, expectedRows, expectedCols); 303 } 304 305 /** 306 * Run through a variety of test configurations with a small scan 307 * @param table 308 * @param reversed 309 * @param rows 310 * @param columns 311 * @throws Exception 312 */ 313 private void testSmallScan(Table table, boolean reversed, int rows, int columns) throws Exception { 314 Scan baseScan = new Scan(); 315 baseScan.setReversed(reversed); 316 baseScan.setSmall(true); 317 318 Scan scan = new Scan(baseScan); 319 verifyExpectedCounts(table, scan, rows, columns); 320 321 scan = new Scan(baseScan); 322 scan.setMaxResultSize(1); 323 verifyExpectedCounts(table, scan, rows, columns); 324 325 scan = new Scan(baseScan); 326 scan.setMaxResultSize(1); 327 scan.setCaching(Integer.MAX_VALUE); 328 verifyExpectedCounts(table, scan, rows, columns); 329 } 330 331 private void verifyExpectedCounts(Table table, Scan scan, int expectedRowCount, 332 int expectedCellCount) throws Exception { 333 ResultScanner scanner = table.getScanner(scan); 334 335 int rowCount = 0; 336 int cellCount = 0; 337 Result r = null; 338 while ((r = scanner.next()) != null) { 339 rowCount++; 340 cellCount += r.rawCells().length; 341 } 342 343 assertTrue("Expected row count: " + expectedRowCount + " Actual row count: " + rowCount, 344 expectedRowCount == rowCount); 345 assertTrue("Expected cell count: " + expectedCellCount + " Actual cell count: " + cellCount, 346 expectedCellCount == cellCount); 347 scanner.close(); 348 } 349 350 /** 351 * Test from client side for get with maxResultPerCF set 352 * 353 * @throws Exception 354 */ 355 @Test 356 public void testGetMaxResults() throws Exception { 357 final TableName tableName = TableName.valueOf(name.getMethodName()); 358 byte [][] FAMILIES = HTestConst.makeNAscii(FAMILY, 3); 359 byte [][] QUALIFIERS = HTestConst.makeNAscii(QUALIFIER, 20); 360 361 Table ht = TEST_UTIL.createTable(tableName, FAMILIES); 362 363 Get get; 364 Put put; 365 Result result; 366 boolean toLog = true; 367 List<Cell> kvListExp; 368 369 kvListExp = new ArrayList<>(); 370 // Insert one CF for row[0] 371 put = new Put(ROW); 372 for (int i=0; i < 10; i++) { 373 KeyValue kv = new KeyValue(ROW, FAMILIES[0], QUALIFIERS[i], 1, VALUE); 374 put.add(kv); 375 kvListExp.add(kv); 376 } 377 ht.put(put); 378 379 get = new Get(ROW); 380 result = ht.get(get); 381 verifyResult(result, kvListExp, toLog, "Testing without setting maxResults"); 382 383 get = new Get(ROW); 384 get.setMaxResultsPerColumnFamily(2); 385 result = ht.get(get); 386 kvListExp = new ArrayList<>(); 387 kvListExp.add(new KeyValue(ROW, FAMILIES[0], QUALIFIERS[0], 1, VALUE)); 388 kvListExp.add(new KeyValue(ROW, FAMILIES[0], QUALIFIERS[1], 1, VALUE)); 389 verifyResult(result, kvListExp, toLog, "Testing basic setMaxResults"); 390 391 // Filters: ColumnRangeFilter 392 get = new Get(ROW); 393 get.setMaxResultsPerColumnFamily(5); 394 get.setFilter(new ColumnRangeFilter(QUALIFIERS[2], true, QUALIFIERS[5], 395 true)); 396 result = ht.get(get); 397 kvListExp = new ArrayList<>(); 398 kvListExp.add(new KeyValue(ROW, FAMILIES[0], QUALIFIERS[2], 1, VALUE)); 399 kvListExp.add(new KeyValue(ROW, FAMILIES[0], QUALIFIERS[3], 1, VALUE)); 400 kvListExp.add(new KeyValue(ROW, FAMILIES[0], QUALIFIERS[4], 1, VALUE)); 401 kvListExp.add(new KeyValue(ROW, FAMILIES[0], QUALIFIERS[5], 1, VALUE)); 402 verifyResult(result, kvListExp, toLog, "Testing single CF with CRF"); 403 404 // Insert two more CF for row[0] 405 // 20 columns for CF2, 10 columns for CF1 406 put = new Put(ROW); 407 for (int i=0; i < QUALIFIERS.length; i++) { 408 KeyValue kv = new KeyValue(ROW, FAMILIES[2], QUALIFIERS[i], 1, VALUE); 409 put.add(kv); 410 } 411 ht.put(put); 412 413 put = new Put(ROW); 414 for (int i=0; i < 10; i++) { 415 KeyValue kv = new KeyValue(ROW, FAMILIES[1], QUALIFIERS[i], 1, VALUE); 416 put.add(kv); 417 } 418 ht.put(put); 419 420 get = new Get(ROW); 421 get.setMaxResultsPerColumnFamily(12); 422 get.addFamily(FAMILIES[1]); 423 get.addFamily(FAMILIES[2]); 424 result = ht.get(get); 425 kvListExp = new ArrayList<>(); 426 //Exp: CF1:q0, ..., q9, CF2: q0, q1, q10, q11, ..., q19 427 for (int i=0; i < 10; i++) { 428 kvListExp.add(new KeyValue(ROW, FAMILIES[1], QUALIFIERS[i], 1, VALUE)); 429 } 430 for (int i=0; i < 2; i++) { 431 kvListExp.add(new KeyValue(ROW, FAMILIES[2], QUALIFIERS[i], 1, VALUE)); 432 } 433 for (int i=10; i < 20; i++) { 434 kvListExp.add(new KeyValue(ROW, FAMILIES[2], QUALIFIERS[i], 1, VALUE)); 435 } 436 verifyResult(result, kvListExp, toLog, "Testing multiple CFs"); 437 438 // Filters: ColumnRangeFilter and ColumnPrefixFilter 439 get = new Get(ROW); 440 get.setMaxResultsPerColumnFamily(3); 441 get.setFilter(new ColumnRangeFilter(QUALIFIERS[2], true, null, true)); 442 result = ht.get(get); 443 kvListExp = new ArrayList<>(); 444 for (int i=2; i < 5; i++) { 445 kvListExp.add(new KeyValue(ROW, FAMILIES[0], QUALIFIERS[i], 1, VALUE)); 446 } 447 for (int i=2; i < 5; i++) { 448 kvListExp.add(new KeyValue(ROW, FAMILIES[1], QUALIFIERS[i], 1, VALUE)); 449 } 450 for (int i=2; i < 5; i++) { 451 kvListExp.add(new KeyValue(ROW, FAMILIES[2], QUALIFIERS[i], 1, VALUE)); 452 } 453 verifyResult(result, kvListExp, toLog, "Testing multiple CFs + CRF"); 454 455 get = new Get(ROW); 456 get.setMaxResultsPerColumnFamily(7); 457 get.setFilter(new ColumnPrefixFilter(QUALIFIERS[1])); 458 result = ht.get(get); 459 kvListExp = new ArrayList<>(); 460 kvListExp.add(new KeyValue(ROW, FAMILIES[0], QUALIFIERS[1], 1, VALUE)); 461 kvListExp.add(new KeyValue(ROW, FAMILIES[1], QUALIFIERS[1], 1, VALUE)); 462 kvListExp.add(new KeyValue(ROW, FAMILIES[2], QUALIFIERS[1], 1, VALUE)); 463 for (int i=10; i < 16; i++) { 464 kvListExp.add(new KeyValue(ROW, FAMILIES[2], QUALIFIERS[i], 1, VALUE)); 465 } 466 verifyResult(result, kvListExp, toLog, "Testing multiple CFs + PFF"); 467 468 } 469 470 /** 471 * Test from client side for scan with maxResultPerCF set 472 * 473 * @throws Exception 474 */ 475 @Test 476 public void testScanMaxResults() throws Exception { 477 final TableName tableName = TableName.valueOf(name.getMethodName()); 478 byte [][] ROWS = HTestConst.makeNAscii(ROW, 2); 479 byte [][] FAMILIES = HTestConst.makeNAscii(FAMILY, 3); 480 byte [][] QUALIFIERS = HTestConst.makeNAscii(QUALIFIER, 10); 481 482 Table ht = TEST_UTIL.createTable(tableName, FAMILIES); 483 484 Put put; 485 Scan scan; 486 Result result; 487 boolean toLog = true; 488 List<Cell> kvListExp, kvListScan; 489 490 kvListExp = new ArrayList<>(); 491 492 for (int r=0; r < ROWS.length; r++) { 493 put = new Put(ROWS[r]); 494 for (int c=0; c < FAMILIES.length; c++) { 495 for (int q=0; q < QUALIFIERS.length; q++) { 496 KeyValue kv = new KeyValue(ROWS[r], FAMILIES[c], QUALIFIERS[q], 1, VALUE); 497 put.add(kv); 498 if (q < 4) { 499 kvListExp.add(kv); 500 } 501 } 502 } 503 ht.put(put); 504 } 505 506 scan = new Scan(); 507 scan.setMaxResultsPerColumnFamily(4); 508 ResultScanner scanner = ht.getScanner(scan); 509 kvListScan = new ArrayList<>(); 510 while ((result = scanner.next()) != null) { 511 for (Cell kv : result.listCells()) { 512 kvListScan.add(kv); 513 } 514 } 515 result = Result.create(kvListScan); 516 verifyResult(result, kvListExp, toLog, "Testing scan with maxResults"); 517 518 } 519 520 /** 521 * Test from client side for get with rowOffset 522 * 523 * @throws Exception 524 */ 525 @Test 526 public void testGetRowOffset() throws Exception { 527 final TableName tableName = TableName.valueOf(name.getMethodName()); 528 byte [][] FAMILIES = HTestConst.makeNAscii(FAMILY, 3); 529 byte [][] QUALIFIERS = HTestConst.makeNAscii(QUALIFIER, 20); 530 531 Table ht = TEST_UTIL.createTable(tableName, FAMILIES); 532 533 Get get; 534 Put put; 535 Result result; 536 boolean toLog = true; 537 List<Cell> kvListExp; 538 539 // Insert one CF for row 540 kvListExp = new ArrayList<>(); 541 put = new Put(ROW); 542 for (int i=0; i < 10; i++) { 543 KeyValue kv = new KeyValue(ROW, FAMILIES[0], QUALIFIERS[i], 1, VALUE); 544 put.add(kv); 545 // skipping first two kvs 546 if (i < 2) continue; 547 kvListExp.add(kv); 548 } 549 ht.put(put); 550 551 //setting offset to 2 552 get = new Get(ROW); 553 get.setRowOffsetPerColumnFamily(2); 554 result = ht.get(get); 555 verifyResult(result, kvListExp, toLog, "Testing basic setRowOffset"); 556 557 //setting offset to 20 558 get = new Get(ROW); 559 get.setRowOffsetPerColumnFamily(20); 560 result = ht.get(get); 561 kvListExp = new ArrayList<>(); 562 verifyResult(result, kvListExp, toLog, "Testing offset > #kvs"); 563 564 //offset + maxResultPerCF 565 get = new Get(ROW); 566 get.setRowOffsetPerColumnFamily(4); 567 get.setMaxResultsPerColumnFamily(5); 568 result = ht.get(get); 569 kvListExp = new ArrayList<>(); 570 for (int i=4; i < 9; i++) { 571 kvListExp.add(new KeyValue(ROW, FAMILIES[0], QUALIFIERS[i], 1, VALUE)); 572 } 573 verifyResult(result, kvListExp, toLog, 574 "Testing offset + setMaxResultsPerCF"); 575 576 // Filters: ColumnRangeFilter 577 get = new Get(ROW); 578 get.setRowOffsetPerColumnFamily(1); 579 get.setFilter(new ColumnRangeFilter(QUALIFIERS[2], true, QUALIFIERS[5], 580 true)); 581 result = ht.get(get); 582 kvListExp = new ArrayList<>(); 583 kvListExp.add(new KeyValue(ROW, FAMILIES[0], QUALIFIERS[3], 1, VALUE)); 584 kvListExp.add(new KeyValue(ROW, FAMILIES[0], QUALIFIERS[4], 1, VALUE)); 585 kvListExp.add(new KeyValue(ROW, FAMILIES[0], QUALIFIERS[5], 1, VALUE)); 586 verifyResult(result, kvListExp, toLog, "Testing offset with CRF"); 587 588 // Insert into two more CFs for row 589 // 10 columns for CF2, 10 columns for CF1 590 for(int j=2; j > 0; j--) { 591 put = new Put(ROW); 592 for (int i=0; i < 10; i++) { 593 KeyValue kv = new KeyValue(ROW, FAMILIES[j], QUALIFIERS[i], 1, VALUE); 594 put.add(kv); 595 } 596 ht.put(put); 597 } 598 599 get = new Get(ROW); 600 get.setRowOffsetPerColumnFamily(4); 601 get.setMaxResultsPerColumnFamily(2); 602 get.addFamily(FAMILIES[1]); 603 get.addFamily(FAMILIES[2]); 604 result = ht.get(get); 605 kvListExp = new ArrayList<>(); 606 //Exp: CF1:q4, q5, CF2: q4, q5 607 kvListExp.add(new KeyValue(ROW, FAMILIES[1], QUALIFIERS[4], 1, VALUE)); 608 kvListExp.add(new KeyValue(ROW, FAMILIES[1], QUALIFIERS[5], 1, VALUE)); 609 kvListExp.add(new KeyValue(ROW, FAMILIES[2], QUALIFIERS[4], 1, VALUE)); 610 kvListExp.add(new KeyValue(ROW, FAMILIES[2], QUALIFIERS[5], 1, VALUE)); 611 verifyResult(result, kvListExp, toLog, 612 "Testing offset + multiple CFs + maxResults"); 613 } 614 615 @Test 616 public void testScanRawDeleteFamilyVersion() throws Exception { 617 TableName tableName = TableName.valueOf(name.getMethodName()); 618 TEST_UTIL.createTable(tableName, FAMILY); 619 Configuration conf = new Configuration(TEST_UTIL.getConfiguration()); 620 conf.set(RPC_CODEC_CONF_KEY, ""); 621 conf.set(DEFAULT_CODEC_CLASS, ""); 622 try (Connection connection = ConnectionFactory.createConnection(conf); 623 Table table = connection.getTable(tableName)) { 624 Delete delete = new Delete(ROW); 625 delete.addFamilyVersion(FAMILY, 0L); 626 table.delete(delete); 627 Scan scan = new Scan(ROW).setRaw(true); 628 ResultScanner scanner = table.getScanner(scan); 629 int count = 0; 630 while (scanner.next() != null) { 631 count++; 632 } 633 assertEquals(1, count); 634 } finally { 635 TEST_UTIL.deleteTable(tableName); 636 } 637 } 638 639 /** 640 * Test from client side for scan while the region is reopened 641 * on the same region server. 642 * 643 * @throws Exception 644 */ 645 @Test 646 public void testScanOnReopenedRegion() throws Exception { 647 final TableName tableName = TableName.valueOf(name.getMethodName()); 648 byte [][] QUALIFIERS = HTestConst.makeNAscii(QUALIFIER, 2); 649 650 Table ht = TEST_UTIL.createTable(tableName, FAMILY); 651 652 Put put; 653 Scan scan; 654 Result result; 655 ResultScanner scanner; 656 boolean toLog = false; 657 List<Cell> kvListExp; 658 659 // table: row, family, c0:0, c1:1 660 put = new Put(ROW); 661 for (int i=0; i < QUALIFIERS.length; i++) { 662 KeyValue kv = new KeyValue(ROW, FAMILY, QUALIFIERS[i], i, VALUE); 663 put.add(kv); 664 } 665 ht.put(put); 666 667 scan = new Scan().withStartRow(ROW); 668 scanner = ht.getScanner(scan); 669 670 HRegionLocation loc; 671 672 try (RegionLocator locator = TEST_UTIL.getConnection().getRegionLocator(tableName)) { 673 loc = locator.getRegionLocation(ROW); 674 } 675 HRegionInfo hri = loc.getRegionInfo(); 676 MiniHBaseCluster cluster = TEST_UTIL.getMiniHBaseCluster(); 677 byte[] regionName = hri.getRegionName(); 678 int i = cluster.getServerWith(regionName); 679 HRegionServer rs = cluster.getRegionServer(i); 680 LOG.info("Unassigning " + hri); 681 TEST_UTIL.getAdmin().unassign(hri.getRegionName(), true); 682 long startTime = EnvironmentEdgeManager.currentTime(); 683 long timeOut = 10000; 684 boolean offline = false; 685 while (true) { 686 if (rs.getOnlineRegion(regionName) == null) { 687 offline = true; 688 break; 689 } 690 assertTrue("Timed out in closing the testing region", 691 EnvironmentEdgeManager.currentTime() < startTime + timeOut); 692 } 693 assertTrue(offline); 694 LOG.info("Assigning " + hri); 695 TEST_UTIL.getAdmin().assign(hri.getRegionName()); 696 startTime = EnvironmentEdgeManager.currentTime(); 697 while (true) { 698 rs = cluster.getRegionServer(cluster.getServerWith(regionName)); 699 if (rs != null && rs.getOnlineRegion(regionName) != null) { 700 offline = false; 701 break; 702 } 703 assertTrue("Timed out in open the testing region", 704 EnvironmentEdgeManager.currentTime() < startTime + timeOut); 705 } 706 assertFalse(offline); 707 708 // c0:0, c1:1 709 kvListExp = new ArrayList<>(); 710 kvListExp.add(new KeyValue(ROW, FAMILY, QUALIFIERS[0], 0, VALUE)); 711 kvListExp.add(new KeyValue(ROW, FAMILY, QUALIFIERS[1], 1, VALUE)); 712 result = scanner.next(); 713 verifyResult(result, kvListExp, toLog, "Testing scan on re-opened region"); 714 } 715 716 @Test 717 public void testAsyncScannerWithSmallData() throws Exception { 718 testAsyncScanner(TableName.valueOf(name.getMethodName()), 719 2, 720 3, 721 10, 722 -1, 723 null); 724 } 725 726 @Test 727 public void testAsyncScannerWithManyRows() throws Exception { 728 testAsyncScanner(TableName.valueOf(name.getMethodName()), 729 30000, 730 1, 731 1, 732 -1, 733 null); 734 } 735 736 @Test 737 public void testAsyncScannerWithoutCaching() throws Exception { 738 testAsyncScanner(TableName.valueOf(name.getMethodName()), 739 5, 740 1, 741 1, 742 1, 743 (b) -> { 744 try { 745 TimeUnit.MILLISECONDS.sleep(500); 746 } catch (InterruptedException ex) { 747 } 748 }); 749 } 750 751 private void testAsyncScanner(TableName table, int rowNumber, int familyNumber, 752 int qualifierNumber, int caching, Consumer<Boolean> listener) throws Exception { 753 assert rowNumber > 0; 754 assert familyNumber > 0; 755 assert qualifierNumber > 0; 756 byte[] row = Bytes.toBytes("r"); 757 byte[] family = Bytes.toBytes("f"); 758 byte[] qualifier = Bytes.toBytes("q"); 759 byte[][] rows = makeNAsciiWithZeroPrefix(row, rowNumber); 760 byte[][] families = makeNAsciiWithZeroPrefix(family, familyNumber); 761 byte[][] qualifiers = makeNAsciiWithZeroPrefix(qualifier, qualifierNumber); 762 763 Table ht = TEST_UTIL.createTable(table, families); 764 765 boolean toLog = true; 766 List<Cell> kvListExp = new ArrayList<>(); 767 768 List<Put> puts = new ArrayList<>(); 769 for (byte[] r : rows) { 770 Put put = new Put(r); 771 for (byte[] f : families) { 772 for (byte[] q : qualifiers) { 773 KeyValue kv = new KeyValue(r, f, q, 1, VALUE); 774 put.add(kv); 775 kvListExp.add(kv); 776 } 777 } 778 puts.add(put); 779 if (puts.size() > 1000) { 780 ht.put(puts); 781 puts.clear(); 782 } 783 } 784 if (!puts.isEmpty()) { 785 ht.put(puts); 786 puts.clear(); 787 } 788 789 Scan scan = new Scan(); 790 scan.setAsyncPrefetch(true); 791 if (caching > 0) { 792 scan.setCaching(caching); 793 } 794 try (ResultScanner scanner = ht.getScanner(scan)) { 795 assertTrue("Not instance of async scanner",scanner instanceof ClientAsyncPrefetchScanner); 796 ((ClientAsyncPrefetchScanner) scanner).setPrefetchListener(listener); 797 List<Cell> kvListScan = new ArrayList<>(); 798 Result result; 799 boolean first = true; 800 int actualRows = 0; 801 while ((result = scanner.next()) != null) { 802 ++actualRows; 803 // waiting for cache. see HBASE-17376 804 if (first) { 805 TimeUnit.SECONDS.sleep(1); 806 first = false; 807 } 808 for (Cell kv : result.listCells()) { 809 kvListScan.add(kv); 810 } 811 } 812 assertEquals(rowNumber, actualRows); 813 // These cells may have different rows but it is ok. The Result#getRow 814 // isn't used in the verifyResult() 815 result = Result.create(kvListScan); 816 verifyResult(result, kvListExp, toLog, "Testing async scan"); 817 } 818 819 TEST_UTIL.deleteTable(table); 820 } 821 822 private static byte[][] makeNAsciiWithZeroPrefix(byte[] base, int n) { 823 int maxLength = Integer.toString(n).length(); 824 byte [][] ret = new byte[n][]; 825 for (int i = 0; i < n; i++) { 826 int length = Integer.toString(i).length(); 827 StringBuilder buf = new StringBuilder(Integer.toString(i)); 828 IntStream.range(0, maxLength - length).forEach(v -> buf.insert(0, "0")); 829 byte[] tail = Bytes.toBytes(buf.toString()); 830 ret[i] = Bytes.add(base, tail); 831 } 832 return ret; 833 } 834 835 static void verifyResult(Result result, List<Cell> expKvList, boolean toLog, 836 String msg) { 837 838 LOG.info(msg); 839 LOG.info("Expected count: " + expKvList.size()); 840 LOG.info("Actual count: " + result.size()); 841 if (expKvList.isEmpty()) 842 return; 843 844 int i = 0; 845 for (Cell kv : result.rawCells()) { 846 if (i >= expKvList.size()) { 847 break; // we will check the size later 848 } 849 850 Cell kvExp = expKvList.get(i++); 851 if (toLog) { 852 LOG.info("get kv is: " + kv.toString()); 853 LOG.info("exp kv is: " + kvExp.toString()); 854 } 855 assertTrue("Not equal", kvExp.equals(kv)); 856 } 857 858 assertEquals(expKvList.size(), result.size()); 859 } 860 861 @Test 862 public void testReadExpiredDataForRawScan() throws IOException { 863 TableName tableName = TableName.valueOf(name.getMethodName()); 864 long ts = System.currentTimeMillis() - 10000; 865 byte[] value = Bytes.toBytes("expired"); 866 try (Table table = TEST_UTIL.createTable(tableName, FAMILY)) { 867 table.put(new Put(ROW).addColumn(FAMILY, QUALIFIER, ts, value)); 868 assertArrayEquals(value, table.get(new Get(ROW)).getValue(FAMILY, QUALIFIER)); 869 TEST_UTIL.getAdmin().modifyColumnFamily(tableName, 870 new HColumnDescriptor(FAMILY).setTimeToLive(5)); 871 try (ResultScanner scanner = table.getScanner(FAMILY)) { 872 assertNull(scanner.next()); 873 } 874 try (ResultScanner scanner = table.getScanner(new Scan().setRaw(true))) { 875 assertArrayEquals(value, scanner.next().getValue(FAMILY, QUALIFIER)); 876 assertNull(scanner.next()); 877 } 878 } 879 } 880 881 @Test 882 public void testScanWithColumnsAndFilterAndVersion() throws IOException { 883 TableName tableName = TableName.valueOf(name.getMethodName()); 884 try (Table table = TEST_UTIL.createTable(tableName, FAMILY, 4)) { 885 for (int i = 0; i < 4; i++) { 886 Put put = new Put(ROW); 887 put.addColumn(FAMILY, QUALIFIER, VALUE); 888 table.put(put); 889 } 890 891 Scan scan = new Scan(); 892 scan.addColumn(FAMILY, QUALIFIER); 893 scan.setFilter(new QualifierFilter(CompareOperator.EQUAL, new BinaryComparator(QUALIFIER))); 894 scan.readVersions(3); 895 896 try (ResultScanner scanner = table.getScanner(scan)) { 897 Result result = scanner.next(); 898 assertEquals(3, result.size()); 899 } 900 } 901 } 902 903 @Test 904 public void testScanWithSameStartRowStopRow() throws IOException { 905 TableName tableName = TableName.valueOf(name.getMethodName()); 906 try (Table table = TEST_UTIL.createTable(tableName, FAMILY)) { 907 table.put(new Put(ROW).addColumn(FAMILY, QUALIFIER, VALUE)); 908 909 Scan scan = new Scan().withStartRow(ROW).withStopRow(ROW); 910 try (ResultScanner scanner = table.getScanner(scan)) { 911 assertNull(scanner.next()); 912 } 913 914 scan = new Scan().withStartRow(ROW, true).withStopRow(ROW, true); 915 try (ResultScanner scanner = table.getScanner(scan)) { 916 Result result = scanner.next(); 917 assertNotNull(result); 918 assertArrayEquals(ROW, result.getRow()); 919 assertArrayEquals(VALUE, result.getValue(FAMILY, QUALIFIER)); 920 assertNull(scanner.next()); 921 } 922 923 scan = new Scan().withStartRow(ROW, true).withStopRow(ROW, false); 924 try (ResultScanner scanner = table.getScanner(scan)) { 925 assertNull(scanner.next()); 926 } 927 928 scan = new Scan().withStartRow(ROW, false).withStopRow(ROW, false); 929 try (ResultScanner scanner = table.getScanner(scan)) { 930 assertNull(scanner.next()); 931 } 932 933 scan = new Scan().withStartRow(ROW, false).withStopRow(ROW, true); 934 try (ResultScanner scanner = table.getScanner(scan)) { 935 assertNull(scanner.next()); 936 } 937 } 938 } 939 940 @Test 941 public void testReverseScanWithFlush() throws Exception { 942 TableName tableName = TableName.valueOf(name.getMethodName()); 943 final int BATCH_SIZE = 10; 944 final int ROWS_TO_INSERT = 100; 945 final byte[] LARGE_VALUE = generateHugeValue(128 * 1024); 946 947 try (Table table = TEST_UTIL.createTable(tableName, FAMILY); 948 Admin admin = TEST_UTIL.getAdmin()) { 949 List<Put> putList = new ArrayList<>(); 950 for (long i = 0; i < ROWS_TO_INSERT; i++) { 951 Put put = new Put(Bytes.toBytes(i)); 952 put.addColumn(FAMILY, QUALIFIER, LARGE_VALUE); 953 putList.add(put); 954 955 if (putList.size() >= BATCH_SIZE) { 956 table.put(putList); 957 admin.flush(tableName); 958 putList.clear(); 959 } 960 } 961 962 if (!putList.isEmpty()) { 963 table.put(putList); 964 admin.flush(tableName); 965 putList.clear(); 966 } 967 968 Scan scan = new Scan(); 969 scan.setReversed(true); 970 int count = 0; 971 972 try (ResultScanner results = table.getScanner(scan)) { 973 for (Result result : results) { 974 count++; 975 } 976 } 977 assertEquals("Expected " + ROWS_TO_INSERT + " rows in the table but it is " + count, 978 ROWS_TO_INSERT, count); 979 } 980 } 981}