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.thrift; 019 020import static org.junit.Assert.assertArrayEquals; 021import static org.junit.Assert.assertEquals; 022import static org.junit.Assert.assertFalse; 023import static org.junit.Assert.assertTrue; 024import static org.junit.Assert.fail; 025 026import java.io.IOException; 027import java.nio.ByteBuffer; 028import java.util.ArrayList; 029import java.util.Collection; 030import java.util.HashMap; 031import java.util.List; 032import java.util.Map; 033import org.apache.hadoop.conf.Configuration; 034import org.apache.hadoop.hbase.CompatibilityFactory; 035import org.apache.hadoop.hbase.HBaseClassTestRule; 036import org.apache.hadoop.hbase.HBaseTestingUtility; 037import org.apache.hadoop.hbase.HColumnDescriptor; 038import org.apache.hadoop.hbase.HConstants; 039import org.apache.hadoop.hbase.HRegionInfo; 040import org.apache.hadoop.hbase.HTableDescriptor; 041import org.apache.hadoop.hbase.TableName; 042import org.apache.hadoop.hbase.client.Put; 043import org.apache.hadoop.hbase.client.Table; 044import org.apache.hadoop.hbase.filter.ParseFilter; 045import org.apache.hadoop.hbase.security.UserProvider; 046import org.apache.hadoop.hbase.test.MetricsAssertHelper; 047import org.apache.hadoop.hbase.testclassification.ClientTests; 048import org.apache.hadoop.hbase.testclassification.LargeTests; 049import org.apache.hadoop.hbase.thrift.ThriftServerRunner.HBaseHandler; 050import org.apache.hadoop.hbase.thrift.generated.BatchMutation; 051import org.apache.hadoop.hbase.thrift.generated.ColumnDescriptor; 052import org.apache.hadoop.hbase.thrift.generated.Hbase; 053import org.apache.hadoop.hbase.thrift.generated.IOError; 054import org.apache.hadoop.hbase.thrift.generated.Mutation; 055import org.apache.hadoop.hbase.thrift.generated.TAppend; 056import org.apache.hadoop.hbase.thrift.generated.TCell; 057import org.apache.hadoop.hbase.thrift.generated.TIncrement; 058import org.apache.hadoop.hbase.thrift.generated.TRegionInfo; 059import org.apache.hadoop.hbase.thrift.generated.TRowResult; 060import org.apache.hadoop.hbase.thrift.generated.TScan; 061import org.apache.hadoop.hbase.util.Bytes; 062import org.apache.hadoop.hbase.util.Threads; 063import org.junit.AfterClass; 064import org.junit.BeforeClass; 065import org.junit.ClassRule; 066import org.junit.Rule; 067import org.junit.Test; 068import org.junit.experimental.categories.Category; 069import org.junit.rules.TestName; 070import org.slf4j.Logger; 071import org.slf4j.LoggerFactory; 072 073/** 074 * Unit testing for ThriftServerRunner.HBaseHandler, a part of the 075 * org.apache.hadoop.hbase.thrift package. 076 */ 077@Category({ClientTests.class, LargeTests.class}) 078public class TestThriftServer { 079 080 @ClassRule 081 public static final HBaseClassTestRule CLASS_RULE = 082 HBaseClassTestRule.forClass(TestThriftServer.class); 083 084 private static final HBaseTestingUtility UTIL = new HBaseTestingUtility(); 085 private static final Logger LOG = LoggerFactory.getLogger(TestThriftServer.class); 086 private static final MetricsAssertHelper metricsHelper = CompatibilityFactory 087 .getInstance(MetricsAssertHelper.class); 088 protected static final int MAXVERSIONS = 3; 089 090 private static ByteBuffer asByteBuffer(String i) { 091 return ByteBuffer.wrap(Bytes.toBytes(i)); 092 } 093 private static ByteBuffer asByteBuffer(long l) { 094 return ByteBuffer.wrap(Bytes.toBytes(l)); 095 } 096 097 // Static names for tables, columns, rows, and values 098 private static ByteBuffer tableAname = asByteBuffer("tableA"); 099 private static ByteBuffer tableBname = asByteBuffer("tableB"); 100 private static ByteBuffer columnAname = asByteBuffer("columnA:"); 101 private static ByteBuffer columnAAname = asByteBuffer("columnA:A"); 102 private static ByteBuffer columnBname = asByteBuffer("columnB:"); 103 private static ByteBuffer rowAname = asByteBuffer("rowA"); 104 private static ByteBuffer rowBname = asByteBuffer("rowB"); 105 private static ByteBuffer valueAname = asByteBuffer("valueA"); 106 private static ByteBuffer valueBname = asByteBuffer("valueB"); 107 private static ByteBuffer valueCname = asByteBuffer("valueC"); 108 private static ByteBuffer valueDname = asByteBuffer("valueD"); 109 private static ByteBuffer valueEname = asByteBuffer(100l); 110 111 @Rule 112 public TestName name = new TestName(); 113 114 @BeforeClass 115 public static void beforeClass() throws Exception { 116 UTIL.getConfiguration().setBoolean(ThriftServerRunner.COALESCE_INC_KEY, true); 117 UTIL.getConfiguration().setBoolean("hbase.table.sanity.checks", false); 118 UTIL.getConfiguration().setInt("hbase.client.retries.number", 3); 119 UTIL.startMiniCluster(); 120 } 121 122 @AfterClass 123 public static void afterClass() throws Exception { 124 UTIL.shutdownMiniCluster(); 125 } 126 127 /** 128 * Runs all of the tests under a single JUnit test method. We 129 * consolidate all testing to one method because HBaseClusterTestCase 130 * is prone to OutOfMemoryExceptions when there are three or more 131 * JUnit test methods. 132 * 133 * @throws Exception 134 */ 135 @Test 136 public void testAll() throws Exception { 137 // Run all tests 138 doTestTableCreateDrop(); 139 doTestThriftMetrics(); 140 doTestTableMutations(); 141 doTestTableTimestampsAndColumns(); 142 doTestTableScanners(); 143 doTestGetTableRegions(); 144 doTestFilterRegistration(); 145 doTestGetRegionInfo(); 146 doTestIncrements(); 147 doTestAppend(); 148 doTestCheckAndPut(); 149 } 150 151 /** 152 * Tests for creating, enabling, disabling, and deleting tables. Also 153 * tests that creating a table with an invalid column name yields an 154 * IllegalArgument exception. 155 * 156 * @throws Exception 157 */ 158 public void doTestTableCreateDrop() throws Exception { 159 ThriftServerRunner.HBaseHandler handler = 160 new ThriftServerRunner.HBaseHandler(UTIL.getConfiguration(), 161 UserProvider.instantiate(UTIL.getConfiguration())); 162 doTestTableCreateDrop(handler); 163 } 164 165 public static void doTestTableCreateDrop(Hbase.Iface handler) throws Exception { 166 createTestTables(handler); 167 dropTestTables(handler); 168 } 169 170 public static final class MySlowHBaseHandler extends ThriftServerRunner.HBaseHandler 171 implements Hbase.Iface { 172 173 protected MySlowHBaseHandler(Configuration c) 174 throws IOException { 175 super(c, UserProvider.instantiate(c)); 176 } 177 178 @Override 179 public List<ByteBuffer> getTableNames() throws IOError { 180 Threads.sleepWithoutInterrupt(3000); 181 return super.getTableNames(); 182 } 183 } 184 185 /** 186 * TODO: These counts are supposed to be zero but sometimes they are not, they are equal to the 187 * passed in maybe. Investigate why. My guess is they are set by the test that runs just 188 * previous to this one. Sometimes they are cleared. Sometimes not. 189 * @param name 190 * @param maybe 191 * @param metrics 192 * @return 193 */ 194 private int getCurrentCount(final String name, final int maybe, final ThriftMetrics metrics) { 195 int currentCount = 0; 196 try { 197 metricsHelper.assertCounter(name, maybe, metrics.getSource()); 198 LOG.info("Shouldn't this be null? name=" + name + ", equals=" + maybe); 199 currentCount = maybe; 200 } catch (AssertionError e) { 201 // Ignore 202 } 203 return currentCount; 204 } 205 206 /** 207 * Tests if the metrics for thrift handler work correctly 208 */ 209 public void doTestThriftMetrics() throws Exception { 210 LOG.info("START doTestThriftMetrics"); 211 Configuration conf = UTIL.getConfiguration(); 212 ThriftMetrics metrics = getMetrics(conf); 213 Hbase.Iface handler = getHandlerForMetricsTest(metrics, conf); 214 int currentCountCreateTable = getCurrentCount("createTable_num_ops", 2, metrics); 215 int currentCountDeleteTable = getCurrentCount("deleteTable_num_ops", 2, metrics); 216 int currentCountDisableTable = getCurrentCount("disableTable_num_ops", 2, metrics); 217 createTestTables(handler); 218 dropTestTables(handler);; 219 metricsHelper.assertCounter("createTable_num_ops", currentCountCreateTable + 2, 220 metrics.getSource()); 221 metricsHelper.assertCounter("deleteTable_num_ops", currentCountDeleteTable + 2, 222 metrics.getSource()); 223 metricsHelper.assertCounter("disableTable_num_ops", currentCountDisableTable + 2, 224 metrics.getSource()); 225 handler.getTableNames(); // This will have an artificial delay. 226 227 // 3 to 6 seconds (to account for potential slowness), measured in nanoseconds 228 try { 229 metricsHelper.assertGaugeGt("getTableNames_avg_time", 3L * 1000 * 1000 * 1000, metrics.getSource()); 230 metricsHelper.assertGaugeLt("getTableNames_avg_time",6L * 1000 * 1000 * 1000, metrics.getSource()); 231 } catch (AssertionError e) { 232 LOG.info("Fix me! Why does this happen? A concurrent cluster running?", e); 233 } 234 } 235 236 private static Hbase.Iface getHandlerForMetricsTest(ThriftMetrics metrics, Configuration conf) 237 throws Exception { 238 Hbase.Iface handler = new MySlowHBaseHandler(conf); 239 return HbaseHandlerMetricsProxy.newInstance(handler, metrics, conf); 240 } 241 242 private static ThriftMetrics getMetrics(Configuration conf) throws Exception { 243 return new ThriftMetrics( conf, ThriftMetrics.ThriftServerType.ONE); 244 } 245 246 247 public static void createTestTables(Hbase.Iface handler) throws Exception { 248 // Create/enable/disable/delete tables, ensure methods act correctly 249 assertEquals(0, handler.getTableNames().size()); 250 handler.createTable(tableAname, getColumnDescriptors()); 251 assertEquals(1, handler.getTableNames().size()); 252 assertEquals(2, handler.getColumnDescriptors(tableAname).size()); 253 assertTrue(handler.isTableEnabled(tableAname)); 254 handler.createTable(tableBname, getColumnDescriptors()); 255 assertEquals(2, handler.getTableNames().size()); 256 } 257 258 public static void checkTableList(Hbase.Iface handler) throws Exception { 259 assertTrue(handler.getTableNames().contains(tableAname)); 260 } 261 262 public static void dropTestTables(Hbase.Iface handler) throws Exception { 263 handler.disableTable(tableBname); 264 assertFalse(handler.isTableEnabled(tableBname)); 265 handler.deleteTable(tableBname); 266 assertEquals(1, handler.getTableNames().size()); 267 handler.disableTable(tableAname); 268 assertFalse(handler.isTableEnabled(tableAname)); 269 /* TODO Reenable. 270 assertFalse(handler.isTableEnabled(tableAname)); 271 handler.enableTable(tableAname); 272 assertTrue(handler.isTableEnabled(tableAname)); 273 handler.disableTable(tableAname);*/ 274 handler.deleteTable(tableAname); 275 assertEquals(0, handler.getTableNames().size()); 276 } 277 278 public void doTestIncrements() throws Exception { 279 ThriftServerRunner.HBaseHandler handler = 280 new ThriftServerRunner.HBaseHandler(UTIL.getConfiguration(), 281 UserProvider.instantiate(UTIL.getConfiguration())); 282 createTestTables(handler); 283 doTestIncrements(handler); 284 dropTestTables(handler); 285 } 286 287 public static void doTestIncrements(HBaseHandler handler) throws Exception { 288 List<Mutation> mutations = new ArrayList<>(1); 289 mutations.add(new Mutation(false, columnAAname, valueEname, true)); 290 mutations.add(new Mutation(false, columnAname, valueEname, true)); 291 handler.mutateRow(tableAname, rowAname, mutations, null); 292 handler.mutateRow(tableAname, rowBname, mutations, null); 293 294 List<TIncrement> increments = new ArrayList<>(3); 295 increments.add(new TIncrement(tableAname, rowBname, columnAAname, 7)); 296 increments.add(new TIncrement(tableAname, rowBname, columnAAname, 7)); 297 increments.add(new TIncrement(tableAname, rowBname, columnAAname, 7)); 298 299 int numIncrements = 60000; 300 for (int i = 0; i < numIncrements; i++) { 301 handler.increment(new TIncrement(tableAname, rowAname, columnAname, 2)); 302 handler.incrementRows(increments); 303 } 304 305 Thread.sleep(1000); 306 long lv = handler.get(tableAname, rowAname, columnAname, null).get(0).value.getLong(); 307 // Wait on all increments being flushed 308 while (handler.coalescer.getQueueSize() != 0) Threads.sleep(10); 309 assertEquals((100 + (2 * numIncrements)), lv ); 310 311 312 lv = handler.get(tableAname, rowBname, columnAAname, null).get(0).value.getLong(); 313 assertEquals((100 + (3 * 7 * numIncrements)), lv); 314 315 assertTrue(handler.coalescer.getSuccessfulCoalescings() > 0); 316 317 } 318 319 /** 320 * Tests adding a series of Mutations and BatchMutations, including a 321 * delete mutation. Also tests data retrieval, and getting back multiple 322 * versions. 323 * 324 * @throws Exception 325 */ 326 public void doTestTableMutations() throws Exception { 327 ThriftServerRunner.HBaseHandler handler = 328 new ThriftServerRunner.HBaseHandler(UTIL.getConfiguration(), 329 UserProvider.instantiate(UTIL.getConfiguration())); 330 doTestTableMutations(handler); 331 } 332 333 public static void doTestTableMutations(Hbase.Iface handler) throws Exception { 334 // Setup 335 handler.createTable(tableAname, getColumnDescriptors()); 336 337 // Apply a few Mutations to rowA 338 // mutations.add(new Mutation(false, columnAname, valueAname)); 339 // mutations.add(new Mutation(false, columnBname, valueBname)); 340 handler.mutateRow(tableAname, rowAname, getMutations(), null); 341 342 // Assert that the changes were made 343 assertEquals(valueAname, 344 handler.get(tableAname, rowAname, columnAname, null).get(0).value); 345 TRowResult rowResult1 = handler.getRow(tableAname, rowAname, null).get(0); 346 assertEquals(rowAname, rowResult1.row); 347 assertEquals(valueBname, 348 rowResult1.columns.get(columnBname).value); 349 350 // Apply a few BatchMutations for rowA and rowB 351 // rowAmutations.add(new Mutation(true, columnAname, null)); 352 // rowAmutations.add(new Mutation(false, columnBname, valueCname)); 353 // batchMutations.add(new BatchMutation(rowAname, rowAmutations)); 354 // Mutations to rowB 355 // rowBmutations.add(new Mutation(false, columnAname, valueCname)); 356 // rowBmutations.add(new Mutation(false, columnBname, valueDname)); 357 // batchMutations.add(new BatchMutation(rowBname, rowBmutations)); 358 handler.mutateRows(tableAname, getBatchMutations(), null); 359 360 // Assert that changes were made to rowA 361 List<TCell> cells = handler.get(tableAname, rowAname, columnAname, null); 362 assertFalse(cells.size() > 0); 363 assertEquals(valueCname, handler.get(tableAname, rowAname, columnBname, null).get(0).value); 364 List<TCell> versions = handler.getVer(tableAname, rowAname, columnBname, MAXVERSIONS, null); 365 assertEquals(valueCname, versions.get(0).value); 366 assertEquals(valueBname, versions.get(1).value); 367 368 // Assert that changes were made to rowB 369 TRowResult rowResult2 = handler.getRow(tableAname, rowBname, null).get(0); 370 assertEquals(rowBname, rowResult2.row); 371 assertEquals(valueCname, rowResult2.columns.get(columnAname).value); 372 assertEquals(valueDname, rowResult2.columns.get(columnBname).value); 373 374 // Apply some deletes 375 handler.deleteAll(tableAname, rowAname, columnBname, null); 376 handler.deleteAllRow(tableAname, rowBname, null); 377 378 // Assert that the deletes were applied 379 int size = handler.get(tableAname, rowAname, columnBname, null).size(); 380 assertEquals(0, size); 381 size = handler.getRow(tableAname, rowBname, null).size(); 382 assertEquals(0, size); 383 384 // Try null mutation 385 List<Mutation> mutations = new ArrayList<>(1); 386 mutations.add(new Mutation(false, columnAname, null, true)); 387 handler.mutateRow(tableAname, rowAname, mutations, null); 388 TRowResult rowResult3 = handler.getRow(tableAname, rowAname, null).get(0); 389 assertEquals(rowAname, rowResult3.row); 390 assertEquals(0, rowResult3.columns.get(columnAname).value.remaining()); 391 392 // Teardown 393 handler.disableTable(tableAname); 394 handler.deleteTable(tableAname); 395 } 396 397 /** 398 * Similar to testTableMutations(), except Mutations are applied with 399 * specific timestamps and data retrieval uses these timestamps to 400 * extract specific versions of data. 401 * 402 * @throws Exception 403 */ 404 public void doTestTableTimestampsAndColumns() throws Exception { 405 // Setup 406 ThriftServerRunner.HBaseHandler handler = 407 new ThriftServerRunner.HBaseHandler(UTIL.getConfiguration(), 408 UserProvider.instantiate(UTIL.getConfiguration())); 409 handler.createTable(tableAname, getColumnDescriptors()); 410 411 // Apply timestamped Mutations to rowA 412 long time1 = System.currentTimeMillis(); 413 handler.mutateRowTs(tableAname, rowAname, getMutations(), time1, null); 414 415 Thread.sleep(1000); 416 417 // Apply timestamped BatchMutations for rowA and rowB 418 long time2 = System.currentTimeMillis(); 419 handler.mutateRowsTs(tableAname, getBatchMutations(), time2, null); 420 421 // Apply an overlapping timestamped mutation to rowB 422 handler.mutateRowTs(tableAname, rowBname, getMutations(), time2, null); 423 424 // the getVerTs is [inf, ts) so you need to increment one. 425 time1 += 1; 426 time2 += 2; 427 428 // Assert that the timestamp-related methods retrieve the correct data 429 assertEquals(2, handler.getVerTs(tableAname, rowAname, columnBname, time2, 430 MAXVERSIONS, null).size()); 431 assertEquals(1, handler.getVerTs(tableAname, rowAname, columnBname, time1, 432 MAXVERSIONS, null).size()); 433 434 TRowResult rowResult1 = handler.getRowTs(tableAname, rowAname, time1, null).get(0); 435 TRowResult rowResult2 = handler.getRowTs(tableAname, rowAname, time2, null).get(0); 436 // columnA was completely deleted 437 //assertTrue(Bytes.equals(rowResult1.columns.get(columnAname).value, valueAname)); 438 assertEquals(rowResult1.columns.get(columnBname).value, valueBname); 439 assertEquals(rowResult2.columns.get(columnBname).value, valueCname); 440 441 // ColumnAname has been deleted, and will never be visible even with a getRowTs() 442 assertFalse(rowResult2.columns.containsKey(columnAname)); 443 444 List<ByteBuffer> columns = new ArrayList<>(1); 445 columns.add(columnBname); 446 447 rowResult1 = handler.getRowWithColumns(tableAname, rowAname, columns, null).get(0); 448 assertEquals(rowResult1.columns.get(columnBname).value, valueCname); 449 assertFalse(rowResult1.columns.containsKey(columnAname)); 450 451 rowResult1 = handler.getRowWithColumnsTs(tableAname, rowAname, columns, time1, null).get(0); 452 assertEquals(rowResult1.columns.get(columnBname).value, valueBname); 453 assertFalse(rowResult1.columns.containsKey(columnAname)); 454 455 // Apply some timestamped deletes 456 // this actually deletes _everything_. 457 // nukes everything in columnB: forever. 458 handler.deleteAllTs(tableAname, rowAname, columnBname, time1, null); 459 handler.deleteAllRowTs(tableAname, rowBname, time2, null); 460 461 // Assert that the timestamp-related methods retrieve the correct data 462 int size = handler.getVerTs(tableAname, rowAname, columnBname, time1, MAXVERSIONS, null).size(); 463 assertEquals(0, size); 464 465 size = handler.getVerTs(tableAname, rowAname, columnBname, time2, MAXVERSIONS, null).size(); 466 assertEquals(1, size); 467 468 // should be available.... 469 assertEquals(handler.get(tableAname, rowAname, columnBname, null).get(0).value, valueCname); 470 471 assertEquals(0, handler.getRow(tableAname, rowBname, null).size()); 472 473 // Teardown 474 handler.disableTable(tableAname); 475 handler.deleteTable(tableAname); 476 } 477 478 /** 479 * Tests the four different scanner-opening methods (with and without 480 * a stoprow, with and without a timestamp). 481 * 482 * @throws Exception 483 */ 484 public void doTestTableScanners() throws Exception { 485 // Setup 486 ThriftServerRunner.HBaseHandler handler = 487 new ThriftServerRunner.HBaseHandler(UTIL.getConfiguration(), 488 UserProvider.instantiate(UTIL.getConfiguration())); 489 handler.createTable(tableAname, getColumnDescriptors()); 490 491 // Apply timestamped Mutations to rowA 492 long time1 = System.currentTimeMillis(); 493 handler.mutateRowTs(tableAname, rowAname, getMutations(), time1, null); 494 495 // Sleep to assure that 'time1' and 'time2' will be different even with a 496 // coarse grained system timer. 497 Thread.sleep(1000); 498 499 // Apply timestamped BatchMutations for rowA and rowB 500 long time2 = System.currentTimeMillis(); 501 handler.mutateRowsTs(tableAname, getBatchMutations(), time2, null); 502 503 time1 += 1; 504 505 // Test a scanner on all rows and all columns, no timestamp 506 int scanner1 = handler.scannerOpen(tableAname, rowAname, getColumnList(true, true), null); 507 TRowResult rowResult1a = handler.scannerGet(scanner1).get(0); 508 assertEquals(rowResult1a.row, rowAname); 509 // This used to be '1'. I don't know why when we are asking for two columns 510 // and when the mutations above would seem to add two columns to the row. 511 // -- St.Ack 05/12/2009 512 assertEquals(1, rowResult1a.columns.size()); 513 assertEquals(rowResult1a.columns.get(columnBname).value, valueCname); 514 515 TRowResult rowResult1b = handler.scannerGet(scanner1).get(0); 516 assertEquals(rowResult1b.row, rowBname); 517 assertEquals(2, rowResult1b.columns.size()); 518 assertEquals(rowResult1b.columns.get(columnAname).value, valueCname); 519 assertEquals(rowResult1b.columns.get(columnBname).value, valueDname); 520 closeScanner(scanner1, handler); 521 522 // Test a scanner on all rows and all columns, with timestamp 523 int scanner2 = handler.scannerOpenTs(tableAname, rowAname, getColumnList(true, true), time1, null); 524 TRowResult rowResult2a = handler.scannerGet(scanner2).get(0); 525 assertEquals(1, rowResult2a.columns.size()); 526 // column A deleted, does not exist. 527 //assertTrue(Bytes.equals(rowResult2a.columns.get(columnAname).value, valueAname)); 528 assertEquals(rowResult2a.columns.get(columnBname).value, valueBname); 529 closeScanner(scanner2, handler); 530 531 // Test a scanner on the first row and first column only, no timestamp 532 int scanner3 = handler.scannerOpenWithStop(tableAname, rowAname, rowBname, 533 getColumnList(true, false), null); 534 closeScanner(scanner3, handler); 535 536 // Test a scanner on the first row and second column only, with timestamp 537 int scanner4 = handler.scannerOpenWithStopTs(tableAname, rowAname, rowBname, 538 getColumnList(false, true), time1, null); 539 TRowResult rowResult4a = handler.scannerGet(scanner4).get(0); 540 assertEquals(1, rowResult4a.columns.size()); 541 assertEquals(rowResult4a.columns.get(columnBname).value, valueBname); 542 543 // Test scanner using a TScan object once with sortColumns False and once with sortColumns true 544 TScan scanNoSortColumns = new TScan(); 545 scanNoSortColumns.setStartRow(rowAname); 546 scanNoSortColumns.setStopRow(rowBname); 547 548 int scanner5 = handler.scannerOpenWithScan(tableAname , scanNoSortColumns, null); 549 TRowResult rowResult5 = handler.scannerGet(scanner5).get(0); 550 assertEquals(1, rowResult5.columns.size()); 551 assertEquals(rowResult5.columns.get(columnBname).value, valueCname); 552 553 TScan scanSortColumns = new TScan(); 554 scanSortColumns.setStartRow(rowAname); 555 scanSortColumns.setStopRow(rowBname); 556 scanSortColumns = scanSortColumns.setSortColumns(true); 557 558 int scanner6 = handler.scannerOpenWithScan(tableAname ,scanSortColumns, null); 559 TRowResult rowResult6 = handler.scannerGet(scanner6).get(0); 560 assertEquals(1, rowResult6.sortedColumns.size()); 561 assertEquals(rowResult6.sortedColumns.get(0).getCell().value, valueCname); 562 563 List<Mutation> rowBmutations = new ArrayList<>(20); 564 for (int i = 0; i < 20; i++) { 565 rowBmutations.add(new Mutation(false, asByteBuffer("columnA:" + i), valueCname, true)); 566 } 567 ByteBuffer rowC = asByteBuffer("rowC"); 568 handler.mutateRow(tableAname, rowC, rowBmutations, null); 569 570 TScan scanSortMultiColumns = new TScan(); 571 scanSortMultiColumns.setStartRow(rowC); 572 scanSortMultiColumns = scanSortMultiColumns.setSortColumns(true); 573 int scanner7 = handler.scannerOpenWithScan(tableAname, scanSortMultiColumns, null); 574 TRowResult rowResult7 = handler.scannerGet(scanner7).get(0); 575 576 ByteBuffer smallerColumn = asByteBuffer("columnA:"); 577 for (int i = 0; i < 20; i++) { 578 ByteBuffer currentColumn = rowResult7.sortedColumns.get(i).columnName; 579 assertTrue(Bytes.compareTo(smallerColumn.array(), currentColumn.array()) < 0); 580 smallerColumn = currentColumn; 581 } 582 583 TScan reversedScan = new TScan(); 584 reversedScan.setReversed(true); 585 reversedScan.setStartRow(rowBname); 586 reversedScan.setStopRow(rowAname); 587 588 int scanner8 = handler.scannerOpenWithScan(tableAname , reversedScan, null); 589 List<TRowResult> results = handler.scannerGet(scanner8); 590 handler.scannerClose(scanner8); 591 assertEquals(1, results.size()); 592 assertEquals(ByteBuffer.wrap(results.get(0).getRow()), rowBname); 593 594 // Teardown 595 handler.disableTable(tableAname); 596 handler.deleteTable(tableAname); 597 } 598 599 /** 600 * For HBASE-2556 601 * Tests for GetTableRegions 602 * 603 * @throws Exception 604 */ 605 public void doTestGetTableRegions() throws Exception { 606 ThriftServerRunner.HBaseHandler handler = 607 new ThriftServerRunner.HBaseHandler(UTIL.getConfiguration(), 608 UserProvider.instantiate(UTIL.getConfiguration())); 609 doTestGetTableRegions(handler); 610 } 611 612 public static void doTestGetTableRegions(Hbase.Iface handler) 613 throws Exception { 614 assertEquals(0, handler.getTableNames().size()); 615 handler.createTable(tableAname, getColumnDescriptors()); 616 assertEquals(1, handler.getTableNames().size()); 617 List<TRegionInfo> regions = handler.getTableRegions(tableAname); 618 int regionCount = regions.size(); 619 assertEquals("empty table should have only 1 region, " + 620 "but found " + regionCount, 1, regionCount); 621 LOG.info("Region found:" + regions.get(0)); 622 handler.disableTable(tableAname); 623 handler.deleteTable(tableAname); 624 regionCount = handler.getTableRegions(tableAname).size(); 625 assertEquals("non-existing table should have 0 region, " + 626 "but found " + regionCount, 0, regionCount); 627 } 628 629 public void doTestFilterRegistration() throws Exception { 630 Configuration conf = UTIL.getConfiguration(); 631 632 conf.set("hbase.thrift.filters", "MyFilter:filterclass"); 633 634 ThriftServerRunner.registerFilters(conf); 635 636 Map<String, String> registeredFilters = ParseFilter.getAllFilters(); 637 638 assertEquals("filterclass", registeredFilters.get("MyFilter")); 639 } 640 641 public void doTestGetRegionInfo() throws Exception { 642 ThriftServerRunner.HBaseHandler handler = 643 new ThriftServerRunner.HBaseHandler(UTIL.getConfiguration(), 644 UserProvider.instantiate(UTIL.getConfiguration())); 645 doTestGetRegionInfo(handler); 646 } 647 648 public static void doTestGetRegionInfo(Hbase.Iface handler) throws Exception { 649 // Create tableA and add two columns to rowA 650 handler.createTable(tableAname, getColumnDescriptors()); 651 try { 652 handler.mutateRow(tableAname, rowAname, getMutations(), null); 653 byte[] searchRow = HRegionInfo.createRegionName( 654 TableName.valueOf(tableAname.array()), rowAname.array(), 655 HConstants.NINES, false); 656 TRegionInfo regionInfo = handler.getRegionInfo(ByteBuffer.wrap(searchRow)); 657 assertTrue(Bytes.toStringBinary(regionInfo.getName()).startsWith( 658 Bytes.toStringBinary(tableAname))); 659 } finally { 660 handler.disableTable(tableAname); 661 handler.deleteTable(tableAname); 662 } 663 } 664 665 /** 666 * Appends the value to a cell and checks that the cell value is updated properly. 667 * 668 * @throws Exception 669 */ 670 public static void doTestAppend() throws Exception { 671 ThriftServerRunner.HBaseHandler handler = 672 new ThriftServerRunner.HBaseHandler(UTIL.getConfiguration(), 673 UserProvider.instantiate(UTIL.getConfiguration())); 674 handler.createTable(tableAname, getColumnDescriptors()); 675 try { 676 List<Mutation> mutations = new ArrayList<>(1); 677 mutations.add(new Mutation(false, columnAname, valueAname, true)); 678 handler.mutateRow(tableAname, rowAname, mutations, null); 679 680 List<ByteBuffer> columnList = new ArrayList<>(1); 681 columnList.add(columnAname); 682 List<ByteBuffer> valueList = new ArrayList<>(1); 683 valueList.add(valueBname); 684 685 TAppend append = new TAppend(tableAname, rowAname, columnList, valueList); 686 handler.append(append); 687 688 TRowResult rowResult = handler.getRow(tableAname, rowAname, null).get(0); 689 assertEquals(rowAname, rowResult.row); 690 assertArrayEquals(Bytes.add(valueAname.array(), valueBname.array()), 691 rowResult.columns.get(columnAname).value.array()); 692 } finally { 693 handler.disableTable(tableAname); 694 handler.deleteTable(tableAname); 695 } 696 } 697 698 /** 699 * Check that checkAndPut fails if the cell does not exist, then put in the cell, then check that 700 * the checkAndPut succeeds. 701 * 702 * @throws Exception 703 */ 704 public static void doTestCheckAndPut() throws Exception { 705 ThriftServerRunner.HBaseHandler handler = 706 new ThriftServerRunner.HBaseHandler(UTIL.getConfiguration(), 707 UserProvider.instantiate(UTIL.getConfiguration())); 708 handler.createTable(tableAname, getColumnDescriptors()); 709 try { 710 List<Mutation> mutations = new ArrayList<>(1); 711 mutations.add(new Mutation(false, columnAname, valueAname, true)); 712 Mutation putB = (new Mutation(false, columnBname, valueBname, true)); 713 714 assertFalse(handler.checkAndPut(tableAname, rowAname, columnAname, valueAname, putB, null)); 715 716 handler.mutateRow(tableAname, rowAname, mutations, null); 717 718 assertTrue(handler.checkAndPut(tableAname, rowAname, columnAname, valueAname, putB, null)); 719 720 TRowResult rowResult = handler.getRow(tableAname, rowAname, null).get(0); 721 assertEquals(rowAname, rowResult.row); 722 assertEquals(valueBname, rowResult.columns.get(columnBname).value); 723 } finally { 724 handler.disableTable(tableAname); 725 handler.deleteTable(tableAname); 726 } 727 } 728 729 @Test 730 public void testMetricsWithException() throws Exception { 731 String rowkey = "row1"; 732 String family = "f"; 733 String col = "c"; 734 // create a table which will throw exceptions for requests 735 final TableName tableName = TableName.valueOf(name.getMethodName()); 736 HTableDescriptor tableDesc = new HTableDescriptor(tableName); 737 tableDesc.addCoprocessor(ErrorThrowingGetObserver.class.getName()); 738 tableDesc.addFamily(new HColumnDescriptor(family)); 739 740 Table table = UTIL.createTable(tableDesc, null); 741 long now = System.currentTimeMillis(); 742 table.put(new Put(Bytes.toBytes(rowkey)) 743 .addColumn(Bytes.toBytes(family), Bytes.toBytes(col), now, Bytes.toBytes("val1"))); 744 745 Configuration conf = UTIL.getConfiguration(); 746 ThriftMetrics metrics = getMetrics(conf); 747 ThriftServerRunner.HBaseHandler hbaseHandler = 748 new ThriftServerRunner.HBaseHandler(UTIL.getConfiguration(), 749 UserProvider.instantiate(UTIL.getConfiguration())); 750 Hbase.Iface handler = HbaseHandlerMetricsProxy.newInstance(hbaseHandler, metrics, conf); 751 752 ByteBuffer tTableName = asByteBuffer(tableName.getNameAsString()); 753 754 // check metrics increment with a successful get 755 long preGetCounter = metricsHelper.checkCounterExists("getRow_num_ops", metrics.getSource()) ? 756 metricsHelper.getCounter("getRow_num_ops", metrics.getSource()) : 757 0; 758 List<TRowResult> tRowResult = handler.getRow(tTableName, asByteBuffer(rowkey), null); 759 assertEquals(1, tRowResult.size()); 760 TRowResult tResult = tRowResult.get(0); 761 762 TCell expectedColumnValue = new TCell(asByteBuffer("val1"), now); 763 764 assertArrayEquals(Bytes.toBytes(rowkey), tResult.getRow()); 765 Collection<TCell> returnedColumnValues = tResult.getColumns().values(); 766 assertEquals(1, returnedColumnValues.size()); 767 assertEquals(expectedColumnValue, returnedColumnValues.iterator().next()); 768 769 metricsHelper.assertCounter("getRow_num_ops", preGetCounter + 1, metrics.getSource()); 770 771 // check metrics increment when the get throws each exception type 772 for (ErrorThrowingGetObserver.ErrorType type : ErrorThrowingGetObserver.ErrorType.values()) { 773 testExceptionType(handler, metrics, tTableName, rowkey, type); 774 } 775 } 776 777 private void testExceptionType(Hbase.Iface handler, ThriftMetrics metrics, 778 ByteBuffer tTableName, String rowkey, 779 ErrorThrowingGetObserver.ErrorType errorType) throws Exception { 780 long preGetCounter = metricsHelper.getCounter("getRow_num_ops", metrics.getSource()); 781 String exceptionKey = errorType.getMetricName(); 782 long preExceptionCounter = metricsHelper.checkCounterExists(exceptionKey, metrics.getSource()) ? 783 metricsHelper.getCounter(exceptionKey, metrics.getSource()) : 784 0; 785 Map<ByteBuffer, ByteBuffer> attributes = new HashMap<>(); 786 attributes.put(asByteBuffer(ErrorThrowingGetObserver.SHOULD_ERROR_ATTRIBUTE), 787 asByteBuffer(errorType.name())); 788 try { 789 List<TRowResult> tRowResult = handler.getRow(tTableName, asByteBuffer(rowkey), attributes); 790 fail("Get with error attribute should have thrown an exception"); 791 } catch (IOError e) { 792 LOG.info("Received exception: ", e); 793 metricsHelper.assertCounter("getRow_num_ops", preGetCounter + 1, metrics.getSource()); 794 metricsHelper.assertCounter(exceptionKey, preExceptionCounter + 1, metrics.getSource()); 795 } 796 } 797 798 /** 799 * 800 * @return a List of ColumnDescriptors for use in creating a table. Has one 801 * default ColumnDescriptor and one ColumnDescriptor with fewer versions 802 */ 803 private static List<ColumnDescriptor> getColumnDescriptors() { 804 ArrayList<ColumnDescriptor> cDescriptors = new ArrayList<>(2); 805 806 // A default ColumnDescriptor 807 ColumnDescriptor cDescA = new ColumnDescriptor(); 808 cDescA.name = columnAname; 809 cDescriptors.add(cDescA); 810 811 // A slightly customized ColumnDescriptor (only 2 versions) 812 ColumnDescriptor cDescB = new ColumnDescriptor(columnBname, 2, "NONE", 813 false, "NONE", 0, 0, false, -1); 814 cDescriptors.add(cDescB); 815 816 return cDescriptors; 817 } 818 819 /** 820 * 821 * @param includeA whether or not to include columnA 822 * @param includeB whether or not to include columnB 823 * @return a List of column names for use in retrieving a scanner 824 */ 825 private List<ByteBuffer> getColumnList(boolean includeA, boolean includeB) { 826 List<ByteBuffer> columnList = new ArrayList<>(); 827 if (includeA) columnList.add(columnAname); 828 if (includeB) columnList.add(columnBname); 829 return columnList; 830 } 831 832 /** 833 * 834 * @return a List of Mutations for a row, with columnA having valueA 835 * and columnB having valueB 836 */ 837 private static List<Mutation> getMutations() { 838 List<Mutation> mutations = new ArrayList<>(2); 839 mutations.add(new Mutation(false, columnAname, valueAname, true)); 840 mutations.add(new Mutation(false, columnBname, valueBname, true)); 841 return mutations; 842 } 843 844 /** 845 * 846 * @return a List of BatchMutations with the following effects: 847 * (rowA, columnA): delete 848 * (rowA, columnB): place valueC 849 * (rowB, columnA): place valueC 850 * (rowB, columnB): place valueD 851 */ 852 private static List<BatchMutation> getBatchMutations() { 853 List<BatchMutation> batchMutations = new ArrayList<>(3); 854 855 // Mutations to rowA. You can't mix delete and put anymore. 856 List<Mutation> rowAmutations = new ArrayList<>(1); 857 rowAmutations.add(new Mutation(true, columnAname, null, true)); 858 batchMutations.add(new BatchMutation(rowAname, rowAmutations)); 859 860 rowAmutations = new ArrayList<>(1); 861 rowAmutations.add(new Mutation(false, columnBname, valueCname, true)); 862 batchMutations.add(new BatchMutation(rowAname, rowAmutations)); 863 864 // Mutations to rowB 865 List<Mutation> rowBmutations = new ArrayList<>(2); 866 rowBmutations.add(new Mutation(false, columnAname, valueCname, true)); 867 rowBmutations.add(new Mutation(false, columnBname, valueDname, true)); 868 batchMutations.add(new BatchMutation(rowBname, rowBmutations)); 869 870 return batchMutations; 871 } 872 873 /** 874 * Asserts that the passed scanner is exhausted, and then closes 875 * the scanner. 876 * 877 * @param scannerId the scanner to close 878 * @param handler the HBaseHandler interfacing to HBase 879 * @throws Exception 880 */ 881 private void closeScanner( 882 int scannerId, ThriftServerRunner.HBaseHandler handler) throws Exception { 883 handler.scannerGet(scannerId); 884 handler.scannerClose(scannerId); 885 } 886}