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