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.mapreduce; 019 020import static org.junit.Assert.assertEquals; 021import static org.junit.Assert.assertTrue; 022import static org.junit.Assert.fail; 023 024import java.io.ByteArrayOutputStream; 025import java.io.IOException; 026import java.io.PrintStream; 027import java.util.ArrayList; 028import java.util.Arrays; 029import org.apache.hadoop.conf.Configuration; 030import org.apache.hadoop.hbase.HBaseClassTestRule; 031import org.apache.hadoop.hbase.HBaseTestingUtil; 032import org.apache.hadoop.hbase.TableName; 033import org.apache.hadoop.hbase.client.Delete; 034import org.apache.hadoop.hbase.client.Put; 035import org.apache.hadoop.hbase.client.Table; 036import org.apache.hadoop.hbase.testclassification.LargeTests; 037import org.apache.hadoop.hbase.testclassification.MapReduceTests; 038import org.apache.hadoop.hbase.util.Bytes; 039import org.apache.hadoop.hbase.util.EnvironmentEdgeManager; 040import org.apache.hadoop.hbase.util.LauncherSecurityManager; 041import org.apache.hadoop.mapreduce.Counter; 042import org.apache.hadoop.mapreduce.Counters; 043import org.apache.hadoop.mapreduce.Job; 044import org.junit.AfterClass; 045import org.junit.BeforeClass; 046import org.junit.ClassRule; 047import org.junit.Test; 048import org.junit.experimental.categories.Category; 049import org.slf4j.Logger; 050import org.slf4j.LoggerFactory; 051 052/** 053 * Test the rowcounter map reduce job. 054 */ 055@Category({ MapReduceTests.class, LargeTests.class }) 056public class TestRowCounter { 057 058 @ClassRule 059 public static final HBaseClassTestRule CLASS_RULE = 060 HBaseClassTestRule.forClass(TestRowCounter.class); 061 062 private static final Logger LOG = LoggerFactory.getLogger(TestRowCounter.class); 063 private final static HBaseTestingUtil TEST_UTIL = new HBaseTestingUtil(); 064 private final static String TABLE_NAME = "testRowCounter"; 065 private final static String TABLE_NAME_TS_RANGE = "testRowCounter_ts_range"; 066 private final static String COL_FAM = "col_fam"; 067 private final static String COL1 = "c1"; 068 private final static String COL2 = "c2"; 069 private final static String COMPOSITE_COLUMN = "C:A:A"; 070 private final static int TOTAL_ROWS = 10; 071 private final static int ROWS_WITH_ONE_COL = 2; 072 073 /** 074 * @throws java.lang.Exception 075 */ 076 @BeforeClass 077 public static void setUpBeforeClass() throws Exception { 078 TEST_UTIL.startMiniCluster(); 079 Table table = TEST_UTIL.createTable(TableName.valueOf(TABLE_NAME), Bytes.toBytes(COL_FAM)); 080 writeRows(table, TOTAL_ROWS, ROWS_WITH_ONE_COL); 081 table.close(); 082 } 083 084 /** 085 * @throws java.lang.Exception 086 */ 087 @AfterClass 088 public static void tearDownAfterClass() throws Exception { 089 TEST_UTIL.shutdownMiniCluster(); 090 } 091 092 /** 093 * Test a case when no column was specified in command line arguments. 094 */ 095 @Test 096 public void testRowCounterNoColumn() throws Exception { 097 String[] args = new String[] { TABLE_NAME }; 098 runRowCount(args, 10); 099 } 100 101 /** 102 * Test a case when the column specified in command line arguments is exclusive for few rows. 103 */ 104 @Test 105 public void testRowCounterExclusiveColumn() throws Exception { 106 String[] args = new String[] { TABLE_NAME, COL_FAM + ":" + COL1 }; 107 runRowCount(args, 8); 108 } 109 110 /** 111 * Test a case when the column specified in command line arguments is one for which the qualifier 112 * contains colons. 113 */ 114 @Test 115 public void testRowCounterColumnWithColonInQualifier() throws Exception { 116 String[] args = new String[] { TABLE_NAME, COL_FAM + ":" + COMPOSITE_COLUMN }; 117 runRowCount(args, 8); 118 } 119 120 /** 121 * Test a case when the column specified in command line arguments is not part of first KV for a 122 * row. 123 */ 124 @Test 125 public void testRowCounterHiddenColumn() throws Exception { 126 String[] args = new String[] { TABLE_NAME, COL_FAM + ":" + COL2 }; 127 runRowCount(args, 10); 128 } 129 130 /** 131 * Test a case when the column specified in command line arguments is exclusive for few rows and 132 * also a row range filter is specified 133 */ 134 @Test 135 public void testRowCounterColumnAndRowRange() throws Exception { 136 String[] args = new String[] { TABLE_NAME, "--range=\\x00rov,\\x00rox", COL_FAM + ":" + COL1 }; 137 runRowCount(args, 8); 138 } 139 140 /** 141 * Test a case when a range is specified with single range of start-end keys 142 */ 143 @Test 144 public void testRowCounterRowSingleRange() throws Exception { 145 String[] args = new String[] { TABLE_NAME, "--range=\\x00row1,\\x00row3" }; 146 runRowCount(args, 2); 147 } 148 149 /** 150 * Test a case when a range is specified with single range with end key only 151 */ 152 @Test 153 public void testRowCounterRowSingleRangeUpperBound() throws Exception { 154 String[] args = new String[] { TABLE_NAME, "--range=,\\x00row3" }; 155 runRowCount(args, 3); 156 } 157 158 /** 159 * Test a case when a range is specified with two ranges where one range is with end key only 160 */ 161 @Test 162 public void testRowCounterRowMultiRangeUpperBound() throws Exception { 163 String[] args = new String[] { TABLE_NAME, "--range=,\\x00row3;\\x00row5,\\x00row7" }; 164 runRowCount(args, 5); 165 } 166 167 /** 168 * Test a case when a range is specified with multiple ranges of start-end keys 169 */ 170 @Test 171 public void testRowCounterRowMultiRange() throws Exception { 172 String[] args = new String[] { TABLE_NAME, "--range=\\x00row1,\\x00row3;\\x00row5,\\x00row8" }; 173 runRowCount(args, 5); 174 } 175 176 /** 177 * Test a case when a range is specified with multiple ranges of start-end keys; one range is 178 * filled, another two are not 179 */ 180 @Test 181 public void testRowCounterRowMultiEmptyRange() throws Exception { 182 String[] args = new String[] { TABLE_NAME, "--range=\\x00row1,\\x00row3;;" }; 183 runRowCount(args, 2); 184 } 185 186 @Test 187 public void testRowCounter10kRowRange() throws Exception { 188 String tableName = TABLE_NAME + "10k"; 189 190 try ( 191 Table table = TEST_UTIL.createTable(TableName.valueOf(tableName), Bytes.toBytes(COL_FAM))) { 192 writeRows(table, 10000, 0); 193 } 194 String[] args = new String[] { tableName, "--range=\\x00row9872,\\x00row9875" }; 195 runRowCount(args, 3); 196 } 197 198 /** 199 * Test a case when the timerange is specified with --starttime and --endtime options 200 */ 201 @Test 202 public void testRowCounterTimeRange() throws Exception { 203 final byte[] family = Bytes.toBytes(COL_FAM); 204 final byte[] col1 = Bytes.toBytes(COL1); 205 Put put1 = new Put(Bytes.toBytes("row_timerange_" + 1)); 206 Put put2 = new Put(Bytes.toBytes("row_timerange_" + 2)); 207 Put put3 = new Put(Bytes.toBytes("row_timerange_" + 3)); 208 209 long ts; 210 211 // clean up content of TABLE_NAME 212 Table table = 213 TEST_UTIL.createTable(TableName.valueOf(TABLE_NAME_TS_RANGE), Bytes.toBytes(COL_FAM)); 214 215 ts = EnvironmentEdgeManager.currentTime(); 216 put1.addColumn(family, col1, ts, Bytes.toBytes("val1")); 217 table.put(put1); 218 Thread.sleep(100); 219 220 ts = EnvironmentEdgeManager.currentTime(); 221 put2.addColumn(family, col1, ts, Bytes.toBytes("val2")); 222 put3.addColumn(family, col1, ts, Bytes.toBytes("val3")); 223 table.put(put2); 224 table.put(put3); 225 table.close(); 226 227 String[] args = new String[] { TABLE_NAME_TS_RANGE, COL_FAM + ":" + COL1, "--starttime=" + 0, 228 "--endtime=" + ts }; 229 runRowCount(args, 1); 230 231 args = new String[] { TABLE_NAME_TS_RANGE, COL_FAM + ":" + COL1, "--starttime=" + 0, 232 "--endtime=" + (ts - 10) }; 233 runRowCount(args, 1); 234 235 args = new String[] { TABLE_NAME_TS_RANGE, COL_FAM + ":" + COL1, "--starttime=" + ts, 236 "--endtime=" + (ts + 1000) }; 237 runRowCount(args, 2); 238 239 args = new String[] { TABLE_NAME_TS_RANGE, COL_FAM + ":" + COL1, 240 "--starttime=" + (ts - 30 * 1000), "--endtime=" + (ts + 30 * 1000), }; 241 runRowCount(args, 3); 242 } 243 244 /** 245 * Run the RowCounter map reduce job and verify the row count. 246 * @param args the command line arguments to be used for rowcounter job. 247 * @param expectedCount the expected row count (result of map reduce job). 248 */ 249 private void runRowCount(String[] args, int expectedCount) throws Exception { 250 RowCounter rowCounter = new RowCounter(); 251 rowCounter.setConf(TEST_UTIL.getConfiguration()); 252 args = Arrays.copyOf(args, args.length + 1); 253 args[args.length - 1] = "--expectedCount=" + expectedCount; 254 long start = EnvironmentEdgeManager.currentTime(); 255 int result = rowCounter.run(args); 256 long duration = EnvironmentEdgeManager.currentTime() - start; 257 LOG.debug("row count duration (ms): " + duration); 258 assertTrue(result == 0); 259 } 260 261 /** 262 * Run the RowCounter map reduce job and verify the row count. 263 * @param args the command line arguments to be used for rowcounter job. 264 * @param expectedCount the expected row count (result of map reduce job). 265 * @throws Exception in case of any unexpected error. 266 */ 267 private void runCreateSubmittableJobWithArgs(String[] args, int expectedCount) throws Exception { 268 Job job = RowCounter.createSubmittableJob(TEST_UTIL.getConfiguration(), args); 269 long start = EnvironmentEdgeManager.currentTime(); 270 job.waitForCompletion(true); 271 long duration = EnvironmentEdgeManager.currentTime() - start; 272 LOG.debug("row count duration (ms): " + duration); 273 assertTrue(job.isSuccessful()); 274 Counter counter = job.getCounters().findCounter(RowCounter.RowCounterMapper.Counters.ROWS); 275 assertEquals(expectedCount, counter.getValue()); 276 } 277 278 @Test 279 public void testCreateSubmittableJobWithArgsNoColumn() throws Exception { 280 String[] args = new String[] { TABLE_NAME }; 281 runCreateSubmittableJobWithArgs(args, 10); 282 } 283 284 /** 285 * Test a case when the column specified in command line arguments is exclusive for few rows. 286 * @throws Exception in case of any unexpected error. 287 */ 288 @Test 289 public void testCreateSubmittableJobWithArgsExclusiveColumn() throws Exception { 290 String[] args = new String[] { TABLE_NAME, COL_FAM + ":" + COL1 }; 291 runCreateSubmittableJobWithArgs(args, 8); 292 } 293 294 /** 295 * Test a case when the column specified in command line arguments is one for which the qualifier 296 * contains colons. 297 * @throws Exception in case of any unexpected error. 298 */ 299 @Test 300 public void testCreateSubmittableJobWithArgsColumnWithColonInQualifier() throws Exception { 301 String[] args = new String[] { TABLE_NAME, COL_FAM + ":" + COMPOSITE_COLUMN }; 302 runCreateSubmittableJobWithArgs(args, 8); 303 } 304 305 /** 306 * Test a case when the column specified in command line arguments is not part of first KV for a 307 * row. 308 * @throws Exception in case of any unexpected error. 309 */ 310 @Test 311 public void testCreateSubmittableJobWithArgsHiddenColumn() throws Exception { 312 String[] args = new String[] { TABLE_NAME, COL_FAM + ":" + COL2 }; 313 runCreateSubmittableJobWithArgs(args, 10); 314 } 315 316 /** 317 * Test a case when the column specified in command line arguments is exclusive for few rows and 318 * also a row range filter is specified 319 * @throws Exception in case of any unexpected error. 320 */ 321 @Test 322 public void testCreateSubmittableJobWithArgsColumnAndRowRange() throws Exception { 323 String[] args = new String[] { TABLE_NAME, "--range=\\x00rov,\\x00rox", COL_FAM + ":" + COL1 }; 324 runCreateSubmittableJobWithArgs(args, 8); 325 } 326 327 /** 328 * Test a case when a range is specified with single range of start-end keys 329 * @throws Exception in case of any unexpected error. 330 */ 331 @Test 332 public void testCreateSubmittableJobWithArgsRowSingleRange() throws Exception { 333 String[] args = new String[] { TABLE_NAME, "--range=\\x00row1,\\x00row3" }; 334 runCreateSubmittableJobWithArgs(args, 2); 335 } 336 337 /** 338 * Test a case when a range is specified with single range with end key only 339 * @throws Exception in case of any unexpected error. 340 */ 341 @Test 342 public void testCreateSubmittableJobWithArgsRowSingleRangeUpperBound() throws Exception { 343 String[] args = new String[] { TABLE_NAME, "--range=,\\x00row3" }; 344 runCreateSubmittableJobWithArgs(args, 3); 345 } 346 347 /** 348 * Test a case when a range is specified with two ranges where one range is with end key only 349 * @throws Exception in case of any unexpected error. 350 */ 351 @Test 352 public void testCreateSubmittableJobWithArgsRowMultiRangeUpperBound() throws Exception { 353 String[] args = new String[] { TABLE_NAME, "--range=,\\x00row3;\\x00row5,\\x00row7" }; 354 runCreateSubmittableJobWithArgs(args, 5); 355 } 356 357 /** 358 * Test a case when a range is specified with multiple ranges of start-end keys 359 * @throws Exception in case of any unexpected error. 360 */ 361 @Test 362 public void testCreateSubmittableJobWithArgsRowMultiRange() throws Exception { 363 String[] args = new String[] { TABLE_NAME, "--range=\\x00row1,\\x00row3;\\x00row5,\\x00row8" }; 364 runCreateSubmittableJobWithArgs(args, 5); 365 } 366 367 /** 368 * Test a case when a range is specified with multiple ranges of start-end keys; one range is 369 * filled, another two are not 370 * @throws Exception in case of any unexpected error. 371 */ 372 @Test 373 public void testCreateSubmittableJobWithArgsRowMultiEmptyRange() throws Exception { 374 String[] args = new String[] { TABLE_NAME, "--range=\\x00row1,\\x00row3;;" }; 375 runCreateSubmittableJobWithArgs(args, 2); 376 } 377 378 @Test 379 public void testCreateSubmittableJobWithArgs10kRowRange() throws Exception { 380 String tableName = TABLE_NAME + "CreateSubmittableJobWithArgs10kRowRange"; 381 382 try ( 383 Table table = TEST_UTIL.createTable(TableName.valueOf(tableName), Bytes.toBytes(COL_FAM))) { 384 writeRows(table, 10000, 0); 385 } 386 String[] args = new String[] { tableName, "--range=\\x00row9872,\\x00row9875" }; 387 runCreateSubmittableJobWithArgs(args, 3); 388 } 389 390 /** 391 * Test a case when the timerange is specified with --starttime and --endtime options 392 * @throws Exception in case of any unexpected error. 393 */ 394 @Test 395 public void testCreateSubmittableJobWithArgsTimeRange() throws Exception { 396 final byte[] family = Bytes.toBytes(COL_FAM); 397 final byte[] col1 = Bytes.toBytes(COL1); 398 Put put1 = new Put(Bytes.toBytes("row_timerange_" + 1)); 399 Put put2 = new Put(Bytes.toBytes("row_timerange_" + 2)); 400 Put put3 = new Put(Bytes.toBytes("row_timerange_" + 3)); 401 402 long ts; 403 404 String tableName = TABLE_NAME_TS_RANGE + "CreateSubmittableJobWithArgs"; 405 // clean up content of TABLE_NAME 406 Table table = TEST_UTIL.createTable(TableName.valueOf(tableName), Bytes.toBytes(COL_FAM)); 407 408 ts = EnvironmentEdgeManager.currentTime(); 409 put1.addColumn(family, col1, ts, Bytes.toBytes("val1")); 410 table.put(put1); 411 Thread.sleep(100); 412 413 ts = EnvironmentEdgeManager.currentTime(); 414 put2.addColumn(family, col1, ts, Bytes.toBytes("val2")); 415 put3.addColumn(family, col1, ts, Bytes.toBytes("val3")); 416 table.put(put2); 417 table.put(put3); 418 table.close(); 419 420 String[] args = 421 new String[] { tableName, COL_FAM + ":" + COL1, "--starttime=" + 0, "--endtime=" + ts }; 422 runCreateSubmittableJobWithArgs(args, 1); 423 424 args = new String[] { tableName, COL_FAM + ":" + COL1, "--starttime=" + 0, 425 "--endtime=" + (ts - 10) }; 426 runCreateSubmittableJobWithArgs(args, 1); 427 428 args = new String[] { tableName, COL_FAM + ":" + COL1, "--starttime=" + ts, 429 "--endtime=" + (ts + 1000) }; 430 runCreateSubmittableJobWithArgs(args, 2); 431 432 args = new String[] { tableName, COL_FAM + ":" + COL1, "--starttime=" + (ts - 30 * 1000), 433 "--endtime=" + (ts + 30 * 1000), }; 434 runCreateSubmittableJobWithArgs(args, 3); 435 } 436 437 /** 438 * Writes TOTAL_ROWS number of distinct rows in to the table. Few rows have two columns, Few have 439 * one. 440 */ 441 private static void writeRows(Table table, int totalRows, int rowsWithOneCol) throws IOException { 442 final byte[] family = Bytes.toBytes(COL_FAM); 443 final byte[] value = Bytes.toBytes("abcd"); 444 final byte[] col1 = Bytes.toBytes(COL1); 445 final byte[] col2 = Bytes.toBytes(COL2); 446 final byte[] col3 = Bytes.toBytes(COMPOSITE_COLUMN); 447 ArrayList<Put> rowsUpdate = new ArrayList<>(); 448 // write few rows with two columns 449 int i = 0; 450 for (; i < totalRows - rowsWithOneCol; i++) { 451 // Use binary rows values to test for HBASE-15287. 452 byte[] row = Bytes.toBytesBinary("\\x00row" + i); 453 Put put = new Put(row); 454 put.addColumn(family, col1, value); 455 put.addColumn(family, col2, value); 456 put.addColumn(family, col3, value); 457 rowsUpdate.add(put); 458 } 459 460 // write few rows with only one column 461 for (; i < totalRows; i++) { 462 byte[] row = Bytes.toBytes("row" + i); 463 Put put = new Put(row); 464 put.addColumn(family, col2, value); 465 rowsUpdate.add(put); 466 } 467 table.put(rowsUpdate); 468 } 469 470 /** 471 * test main method. Import should print help and call System.exit 472 */ 473 @Test 474 public void testImportMain() throws Exception { 475 SecurityManager SECURITY_MANAGER = System.getSecurityManager(); 476 LauncherSecurityManager newSecurityManager = new LauncherSecurityManager(); 477 System.setSecurityManager(newSecurityManager); 478 String[] args = {}; 479 try { 480 try { 481 RowCounter.main(args); 482 fail("should be SecurityException"); 483 } catch (SecurityException e) { 484 assertEquals(RowCounter.EXIT_FAILURE, newSecurityManager.getExitCode()); 485 } 486 try { 487 args = new String[2]; 488 args[0] = "table"; 489 args[1] = "--range=1"; 490 RowCounter.main(args); 491 fail("should be SecurityException"); 492 } catch (SecurityException e) { 493 assertEquals(RowCounter.EXIT_FAILURE, newSecurityManager.getExitCode()); 494 } 495 496 } finally { 497 System.setSecurityManager(SECURITY_MANAGER); 498 } 499 } 500 501 @Test 502 public void testHelp() throws Exception { 503 PrintStream oldPrintStream = System.out; 504 try { 505 ByteArrayOutputStream data = new ByteArrayOutputStream(); 506 PrintStream stream = new PrintStream(data); 507 System.setOut(stream); 508 String[] args = { "-h" }; 509 runRowCount(args, 0); 510 assertUsageContent(data.toString()); 511 args = new String[] { "--help" }; 512 runRowCount(args, 0); 513 assertUsageContent(data.toString()); 514 } finally { 515 System.setOut(oldPrintStream); 516 } 517 } 518 519 @Test 520 public void testInvalidTable() throws Exception { 521 try { 522 String[] args = { "invalid" }; 523 runRowCount(args, 0); 524 fail("RowCounter should had failed with invalid table."); 525 } catch (Throwable e) { 526 assertTrue(e instanceof AssertionError); 527 } 528 } 529 530 /** 531 * Step 1: Add 10 rows(row1, row2, row3, row4, row5, row6, row7, row8, row9, row10) to a table. 532 * Each row contains 1 column family and 4 columns and values for two different timestamps - 5 & 533 * 10. 534 * <p> 535 * Step 2: Delete the latest version of column A for row1. --> 1 X Delete 536 * <p> 537 * Step 3: Delete the cell for timestamp 5 of column B for row1. --> 1 X Delete 538 * <p> 539 * Step 4: Delete a column family for row2 and row4. --> 2 X DeleteFamily 540 * <p> 541 * Step 5: Delete all versions of a specific column for row3, row5 and row6. --> 3 X DeleteColumn 542 * <p> 543 * Step 6: Delete all columns for timestamp 5 for row 7. --> 1 X DeleteFamilyVersion 544 * <p> 545 * Case 1: Run row counter without countDeleteMarkers and validate counter values. 546 * <p> 547 * Case 2: Run row counter with countDeleteMarkers flag and validate counter values. 548 * <p> 549 * Case 3: Run row counter with countDeleteMarkers flag for a row range and validate counter 550 * values. 551 */ 552 @Test 553 public void testRowCounterWithCountDeleteMarkersOption() throws Exception { 554 // Test Setup 555 556 final TableName tableName = 557 TableName.valueOf(TABLE_NAME + "_" + "withCountDeleteMarkersOption"); 558 // Row keys are represented in this way because of HBASE-15287 559 final byte[][] rowKeys = { Bytes.toBytesBinary("\\x00row1"), Bytes.toBytesBinary("\\x00row2"), 560 Bytes.toBytesBinary("\\x00row3"), Bytes.toBytesBinary("\\x00row4"), 561 Bytes.toBytesBinary("\\x00row5"), Bytes.toBytesBinary("\\x00row6"), 562 Bytes.toBytesBinary("\\x00row7"), Bytes.toBytesBinary("\\x00row8"), 563 Bytes.toBytesBinary("\\x00row9"), Bytes.toBytesBinary("\\x00row10") }; 564 final byte[] columnFamily = Bytes.toBytes("cf"); 565 final byte[][] columns = 566 { Bytes.toBytes("A"), Bytes.toBytes("B"), Bytes.toBytes("C"), Bytes.toBytes("D") }; 567 final byte[][] values = { Bytes.toBytes("a"), Bytes.toBytes("b") }; 568 569 try (Table table = TEST_UTIL.createTable(tableName, columnFamily)) { 570 // Step 1: Insert rows with columns 571 for (byte[] rowKey : rowKeys) { 572 Put put = new Put(rowKey); 573 for (byte[] col : columns) { 574 long timestamp = 5L; 575 for (byte[] value : values) { 576 put.addColumn(columnFamily, col, timestamp, value); 577 timestamp += 5L; 578 } 579 } 580 table.put(put); 581 } 582 TEST_UTIL.getAdmin().flush(tableName); 583 584 // Steps 2-6 585 Delete deleteA = new Delete(rowKeys[0]).addColumn(columnFamily, columns[0]); 586 Delete deleteB = new Delete(rowKeys[0]).addColumn(columnFamily, columns[1], 5L); 587 Delete deleteC = new Delete(rowKeys[1]).addFamily(columnFamily); 588 Delete deleteD = new Delete(rowKeys[2]).addColumns(columnFamily, columns[0]); 589 Delete deleteE = new Delete(rowKeys[3]).addFamily(columnFamily); 590 Delete deleteF = new Delete(rowKeys[4]).addColumns(columnFamily, columns[0]); 591 Delete deleteG = new Delete(rowKeys[5]).addColumns(columnFamily, columns[0]); 592 Delete deleteH = new Delete(rowKeys[6]).addFamilyVersion(columnFamily, 5L); 593 594 table.delete(deleteA); 595 table.delete(deleteB); 596 table.delete(deleteC); 597 table.delete(deleteD); 598 table.delete(deleteE); 599 table.delete(deleteF); 600 table.delete(deleteG); 601 table.delete(deleteH); 602 TEST_UTIL.getAdmin().flush(tableName); 603 } 604 605 RowCounter rowCounterWithoutCountDeleteMarkers = new RowCounter(); 606 RowCounter rowCounterWithCountDeleteMarkers = new RowCounter(); 607 RowCounter rowCounterForRangeWithCountDeleteMarkers = new RowCounter(); 608 rowCounterWithoutCountDeleteMarkers.setConf(new Configuration(TEST_UTIL.getConfiguration())); 609 rowCounterWithCountDeleteMarkers.setConf(new Configuration(TEST_UTIL.getConfiguration())); 610 rowCounterForRangeWithCountDeleteMarkers 611 .setConf(new Configuration(TEST_UTIL.getConfiguration())); 612 613 // Invocation 614 615 rowCounterWithoutCountDeleteMarkers.run(new String[] { tableName.getNameAsString() }); 616 rowCounterWithCountDeleteMarkers 617 .run(new String[] { tableName.getNameAsString(), "--countDeleteMarkers" }); 618 rowCounterForRangeWithCountDeleteMarkers.run(new String[] { tableName.getNameAsString(), 619 "--countDeleteMarkers", "--range=\\x00row8,\\x00row9" }); 620 621 // Validation 622 623 // Case 1: 624 validateCounterCounts(rowCounterWithoutCountDeleteMarkers.getMapReduceJob().getCounters(), 8, 0, 625 0, 0, 0, 0); 626 627 // Case 2: 628 validateCounterCounts(rowCounterWithCountDeleteMarkers.getMapReduceJob().getCounters(), 10, 7, 629 2, 3, 2, 1); 630 631 // Case 3: 632 validateCounterCounts(rowCounterForRangeWithCountDeleteMarkers.getMapReduceJob().getCounters(), 633 1, 0, 0, 0, 0, 0); 634 } 635 636 private void validateCounterCounts(Counters counters, long rowCount, 637 long rowsWithDeleteMarkersCount, long deleteCount, long deleteColumnCount, 638 long deleteFamilyCount, long deleteFamilyVersionCount) { 639 640 long actualRowCount = 641 counters.findCounter(RowCounter.RowCounterMapper.Counters.ROWS).getValue(); 642 long actualRowsWithDeleteMarkersCount = 643 counters.findCounter(RowCounter.RowCounterMapper.Counters.ROWS_WITH_DELETE_MARKER).getValue(); 644 long actualDeleteCount = 645 counters.findCounter(RowCounter.RowCounterMapper.Counters.DELETE).getValue(); 646 long actualDeleteColumnCount = 647 counters.findCounter(RowCounter.RowCounterMapper.Counters.DELETE_COLUMN).getValue(); 648 long actualDeleteFamilyCount = 649 counters.findCounter(RowCounter.RowCounterMapper.Counters.DELETE_FAMILY).getValue(); 650 long actualDeleteFamilyVersionCount = 651 counters.findCounter(RowCounter.RowCounterMapper.Counters.DELETE_FAMILY_VERSION).getValue(); 652 653 assertEquals(rowCount, actualRowCount); 654 assertEquals(rowsWithDeleteMarkersCount, actualRowsWithDeleteMarkersCount); 655 assertEquals(deleteCount, actualDeleteCount); 656 assertEquals(deleteColumnCount, actualDeleteColumnCount); 657 assertEquals(deleteFamilyCount, actualDeleteFamilyCount); 658 assertEquals(deleteFamilyVersionCount, actualDeleteFamilyVersionCount); 659 } 660 661 private void assertUsageContent(String usage) { 662 assertTrue(usage 663 .contains("usage: hbase rowcounter " + "<tablename> [options] [<column1> <column2>...]")); 664 assertTrue(usage.contains("Options:\n")); 665 assertTrue(usage.contains( 666 "--starttime=<arg> " + "starting time filter to start counting rows from.\n")); 667 assertTrue(usage.contains("--endtime=<arg> " 668 + "end time filter limit, to only count rows up to this timestamp.\n")); 669 assertTrue(usage 670 .contains("--range=<arg> " + "[startKey],[endKey][;[startKey],[endKey]...]]\n")); 671 assertTrue(usage.contains("--expectedCount=<arg> expected number of rows to be count.\n")); 672 assertTrue( 673 usage.contains("For performance, " + "consider the following configuration properties:\n")); 674 assertTrue(usage.contains("-Dhbase.client.scanner.caching=100\n")); 675 assertTrue(usage.contains("-Dmapreduce.map.speculative=false\n")); 676 } 677 678}