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; 019 020import static org.junit.Assert.assertArrayEquals; 021import static org.junit.Assert.assertEquals; 022import static org.junit.Assert.assertNotEquals; 023import static org.junit.Assert.assertNotNull; 024import static org.junit.Assert.assertNull; 025import static org.junit.Assert.assertTrue; 026import static org.mockito.ArgumentMatchers.anyObject; 027import static org.mockito.Mockito.doReturn; 028import static org.mockito.Mockito.mock; 029import static org.mockito.Mockito.reset; 030import static org.mockito.Mockito.times; 031import static org.mockito.Mockito.verify; 032 033import java.io.IOException; 034import java.util.ArrayList; 035import java.util.Collections; 036import java.util.HashMap; 037import java.util.List; 038import java.util.Map; 039import java.util.Random; 040import java.util.concurrent.TimeUnit; 041import org.apache.hadoop.conf.Configuration; 042import org.apache.hadoop.hbase.client.Admin; 043import org.apache.hadoop.hbase.client.Connection; 044import org.apache.hadoop.hbase.client.ConnectionFactory; 045import org.apache.hadoop.hbase.client.Get; 046import org.apache.hadoop.hbase.client.Put; 047import org.apache.hadoop.hbase.client.RegionInfo; 048import org.apache.hadoop.hbase.client.RegionInfoBuilder; 049import org.apache.hadoop.hbase.client.RegionLocator; 050import org.apache.hadoop.hbase.client.Result; 051import org.apache.hadoop.hbase.client.Table; 052import org.apache.hadoop.hbase.ipc.CallRunner; 053import org.apache.hadoop.hbase.ipc.DelegatingRpcScheduler; 054import org.apache.hadoop.hbase.ipc.PriorityFunction; 055import org.apache.hadoop.hbase.ipc.RpcScheduler; 056import org.apache.hadoop.hbase.master.HMaster; 057import org.apache.hadoop.hbase.regionserver.HRegion; 058import org.apache.hadoop.hbase.regionserver.HRegionServer; 059import org.apache.hadoop.hbase.regionserver.RSRpcServices; 060import org.apache.hadoop.hbase.regionserver.SimpleRpcSchedulerFactory; 061import org.apache.hadoop.hbase.testclassification.MediumTests; 062import org.apache.hadoop.hbase.testclassification.MiscTests; 063import org.apache.hadoop.hbase.util.Bytes; 064import org.apache.hadoop.hbase.util.EnvironmentEdgeManager; 065import org.apache.hadoop.hbase.util.ManualEnvironmentEdge; 066import org.apache.hadoop.hbase.util.Pair; 067import org.apache.hadoop.hbase.zookeeper.MetaTableLocator; 068import org.junit.AfterClass; 069import org.junit.BeforeClass; 070import org.junit.ClassRule; 071import org.junit.Rule; 072import org.junit.Test; 073import org.junit.experimental.categories.Category; 074import org.junit.rules.TestName; 075import org.slf4j.Logger; 076import org.slf4j.LoggerFactory; 077 078import org.apache.hbase.thirdparty.com.google.common.collect.Lists; 079 080/** 081 * Test {@link org.apache.hadoop.hbase.MetaTableAccessor}. 082 */ 083@Category({MiscTests.class, MediumTests.class}) 084@SuppressWarnings("deprecation") 085public class TestMetaTableAccessor { 086 @ClassRule 087 public static final HBaseClassTestRule CLASS_RULE = 088 HBaseClassTestRule.forClass(TestMetaTableAccessor.class); 089 090 private static final Logger LOG = LoggerFactory.getLogger(TestMetaTableAccessor.class); 091 private static final HBaseTestingUtility UTIL = new HBaseTestingUtility(); 092 private static Connection connection; 093 private Random random = new Random(); 094 095 @Rule 096 public TestName name = new TestName(); 097 098 @BeforeClass public static void beforeClass() throws Exception { 099 UTIL.startMiniCluster(3); 100 101 Configuration c = new Configuration(UTIL.getConfiguration()); 102 // Tests to 4 retries every 5 seconds. Make it try every 1 second so more 103 // responsive. 1 second is default as is ten retries. 104 c.setLong("hbase.client.pause", 1000); 105 c.setInt(HConstants.HBASE_CLIENT_RETRIES_NUMBER, 10); 106 connection = ConnectionFactory.createConnection(c); 107 } 108 109 @AfterClass public static void afterClass() throws Exception { 110 connection.close(); 111 UTIL.shutdownMiniCluster(); 112 } 113 114 /** 115 * Test for HBASE-23044. 116 */ 117 @Test 118 public void testGetMergeRegions() throws Exception { 119 TableName tn = TableName.valueOf(this.name.getMethodName()); 120 UTIL.createMultiRegionTable(tn, Bytes.toBytes("CF"), 4); 121 UTIL.waitTableAvailable(tn); 122 try (Admin admin = UTIL.getAdmin()) { 123 List<RegionInfo> regions = admin.getRegions(tn); 124 assertEquals(4, regions.size()); 125 admin.mergeRegionsAsync(regions.get(0).getRegionName(), regions.get(1).getRegionName(), false) 126 .get(60, TimeUnit.SECONDS); 127 admin.mergeRegionsAsync(regions.get(2).getRegionName(), regions.get(3).getRegionName(), false) 128 .get(60, TimeUnit.SECONDS); 129 130 List<RegionInfo> mergedRegions = admin.getRegions(tn); 131 assertEquals(2, mergedRegions.size()); 132 RegionInfo mergedRegion0 = mergedRegions.get(0); 133 RegionInfo mergedRegion1 = mergedRegions.get(1); 134 135 List<RegionInfo> mergeParents = 136 MetaTableAccessor.getMergeRegions(connection, mergedRegion0.getRegionName()); 137 assertTrue(mergeParents.contains(regions.get(0))); 138 assertTrue(mergeParents.contains(regions.get(1))); 139 mergeParents = MetaTableAccessor.getMergeRegions(connection, mergedRegion1.getRegionName()); 140 assertTrue(mergeParents.contains(regions.get(2))); 141 assertTrue(mergeParents.contains(regions.get(3))); 142 143 // Delete merge qualifiers for mergedRegion0, then cannot getMergeRegions again 144 MetaTableAccessor.deleteMergeQualifiers(connection, mergedRegion0); 145 mergeParents = MetaTableAccessor.getMergeRegions(connection, mergedRegion0.getRegionName()); 146 assertNull(mergeParents); 147 148 mergeParents = MetaTableAccessor.getMergeRegions(connection, mergedRegion1.getRegionName()); 149 assertTrue(mergeParents.contains(regions.get(2))); 150 assertTrue(mergeParents.contains(regions.get(3))); 151 } 152 UTIL.deleteTable(tn); 153 } 154 155 @Test 156 public void testAddMergeRegions() throws IOException { 157 TableName tn = TableName.valueOf(this.name.getMethodName()); 158 Put put = new Put(Bytes.toBytes(this.name.getMethodName())); 159 List<RegionInfo> ris = new ArrayList<>(); 160 int limit = 10; 161 byte [] previous = HConstants.EMPTY_START_ROW; 162 for (int i = 0; i < limit; i++) { 163 RegionInfo ri = RegionInfoBuilder.newBuilder(tn). 164 setStartKey(previous).setEndKey(Bytes.toBytes(i)).build(); 165 ris.add(ri); 166 } 167 put = MetaTableAccessor.addMergeRegions(put, ris); 168 List<Cell> cells = put.getFamilyCellMap().get(HConstants.CATALOG_FAMILY); 169 String previousQualifier = null; 170 assertEquals(limit, cells.size()); 171 for (Cell cell: cells) { 172 LOG.info(cell.toString()); 173 String qualifier = Bytes.toString(cell.getQualifierArray()); 174 assertTrue(qualifier.startsWith(HConstants.MERGE_QUALIFIER_PREFIX_STR)); 175 assertNotEquals(qualifier, previousQualifier); 176 previousQualifier = qualifier; 177 } 178 } 179 180 @Test 181 public void testIsMetaWhenAllHealthy() throws InterruptedException { 182 HMaster m = UTIL.getMiniHBaseCluster().getMaster(); 183 assertTrue(m.waitForMetaOnline()); 184 } 185 186 @Test 187 public void testIsMetaWhenMetaGoesOffline() throws InterruptedException { 188 HMaster m = UTIL.getMiniHBaseCluster().getMaster(); 189 int index = UTIL.getMiniHBaseCluster().getServerWithMeta(); 190 HRegionServer rsWithMeta = UTIL.getMiniHBaseCluster().getRegionServer(index); 191 rsWithMeta.abort("TESTING"); 192 assertTrue(m.waitForMetaOnline()); 193 } 194 195 /** 196 * Does {@link MetaTableAccessor#getRegion(Connection, byte[])} and a write 197 * against hbase:meta while its hosted server is restarted to prove our retrying 198 * works. 199 */ 200 @Test public void testRetrying() 201 throws IOException, InterruptedException { 202 final TableName tableName = TableName.valueOf(name.getMethodName()); 203 LOG.info("Started " + tableName); 204 Table t = UTIL.createMultiRegionTable(tableName, HConstants.CATALOG_FAMILY); 205 int regionCount = -1; 206 try (RegionLocator r = UTIL.getConnection().getRegionLocator(tableName)) { 207 regionCount = r.getStartKeys().length; 208 } 209 // Test it works getting a region from just made user table. 210 final List<RegionInfo> regions = 211 testGettingTableRegions(connection, tableName, regionCount); 212 MetaTask reader = new MetaTask(connection, "reader") { 213 @Override 214 void metaTask() throws Throwable { 215 testGetRegion(connection, regions.get(0)); 216 LOG.info("Read " + regions.get(0).getEncodedName()); 217 } 218 }; 219 MetaTask writer = new MetaTask(connection, "writer") { 220 221 @Override 222 void metaTask() throws IOException { 223 MetaTableAccessor.addRegionsToMeta(connection, Collections.singletonList(regions.get(0)), 224 1); 225 LOG.info("Wrote " + regions.get(0).getEncodedName()); 226 } 227 }; 228 reader.start(); 229 writer.start(); 230 231 // We're gonna check how it takes. If it takes too long, we will consider 232 // it as a fail. We can't put that in the @Test tag as we want to close 233 // the threads nicely 234 final long timeOut = 180000; 235 long startTime = System.currentTimeMillis(); 236 237 try { 238 // Make sure reader and writer are working. 239 assertTrue(reader.isProgressing()); 240 assertTrue(writer.isProgressing()); 241 242 // Kill server hosting meta -- twice . See if our reader/writer ride over the 243 // meta moves. They'll need to retry. 244 for (int i = 0; i < 2; i++) { 245 LOG.info("Restart=" + i); 246 UTIL.ensureSomeRegionServersAvailable(2); 247 int index = -1; 248 do { 249 index = UTIL.getMiniHBaseCluster().getServerWithMeta(); 250 } while (index == -1 && 251 startTime + timeOut < System.currentTimeMillis()); 252 253 if (index != -1){ 254 UTIL.getMiniHBaseCluster().abortRegionServer(index); 255 UTIL.getMiniHBaseCluster().waitOnRegionServer(index); 256 } 257 } 258 259 assertTrue("reader: " + reader.toString(), reader.isProgressing()); 260 assertTrue("writer: " + writer.toString(), writer.isProgressing()); 261 } catch (IOException e) { 262 throw e; 263 } finally { 264 reader.stop = true; 265 writer.stop = true; 266 reader.join(); 267 writer.join(); 268 t.close(); 269 } 270 long exeTime = System.currentTimeMillis() - startTime; 271 assertTrue("Timeout: test took " + exeTime / 1000 + " sec", exeTime < timeOut); 272 } 273 274 /** 275 * Thread that runs a MetaTableAccessor task until asked stop. 276 */ 277 abstract static class MetaTask extends Thread { 278 boolean stop = false; 279 int count = 0; 280 Throwable t = null; 281 final Connection connection; 282 283 MetaTask(final Connection connection, final String name) { 284 super(name); 285 this.connection = connection; 286 } 287 288 @Override 289 public void run() { 290 try { 291 while(!this.stop) { 292 LOG.info("Before " + this.getName()+ ", count=" + this.count); 293 metaTask(); 294 this.count += 1; 295 LOG.info("After " + this.getName() + ", count=" + this.count); 296 Thread.sleep(100); 297 } 298 } catch (Throwable t) { 299 LOG.info(this.getName() + " failed", t); 300 this.t = t; 301 } 302 } 303 304 boolean isProgressing() throws InterruptedException { 305 int currentCount = this.count; 306 while(currentCount == this.count) { 307 if (!isAlive()) return false; 308 if (this.t != null) return false; 309 Thread.sleep(10); 310 } 311 return true; 312 } 313 314 @Override 315 public String toString() { 316 return "count=" + this.count + ", t=" + 317 (this.t == null? "null": this.t.toString()); 318 } 319 320 abstract void metaTask() throws Throwable; 321 } 322 323 @Test 324 public void testGetRegionsFromMetaTable() throws IOException, InterruptedException { 325 List<RegionInfo> regions = MetaTableLocator.getMetaRegions(UTIL.getZooKeeperWatcher()); 326 assertTrue(regions.size() >= 1); 327 assertTrue( 328 MetaTableLocator.getMetaRegionsAndLocations(UTIL.getZooKeeperWatcher()).size() >= 1); 329 } 330 331 @Test 332 public void testGetRegion() throws IOException, InterruptedException { 333 final String name = this.name.getMethodName(); 334 LOG.info("Started " + name); 335 // Test get on non-existent region. 336 Pair<RegionInfo, ServerName> pair = 337 MetaTableAccessor.getRegion(connection, Bytes.toBytes("nonexistent-region")); 338 assertNull(pair); 339 LOG.info("Finished " + name); 340 } 341 342 // Test for the optimization made in HBASE-3650 343 @Test public void testScanMetaForTable() 344 throws IOException, InterruptedException { 345 final TableName tableName = TableName.valueOf(name.getMethodName()); 346 LOG.info("Started " + tableName); 347 348 /** Create 2 tables 349 - testScanMetaForTable 350 - testScanMetaForTablf 351 **/ 352 353 UTIL.createTable(tableName, HConstants.CATALOG_FAMILY); 354 // name that is +1 greater than the first one (e+1=f) 355 TableName greaterName = 356 TableName.valueOf("testScanMetaForTablf"); 357 UTIL.createTable(greaterName, HConstants.CATALOG_FAMILY); 358 359 // Now make sure we only get the regions from 1 of the tables at a time 360 361 assertEquals(1, MetaTableAccessor.getTableRegions(connection, tableName).size()); 362 assertEquals(1, MetaTableAccessor.getTableRegions(connection, greaterName).size()); 363 } 364 365 private static List<RegionInfo> testGettingTableRegions(final Connection connection, 366 final TableName name, final int regionCount) 367 throws IOException, InterruptedException { 368 List<RegionInfo> regions = MetaTableAccessor.getTableRegions(connection, name); 369 assertEquals(regionCount, regions.size()); 370 Pair<RegionInfo, ServerName> pair = 371 MetaTableAccessor.getRegion(connection, regions.get(0).getRegionName()); 372 assertEquals(regions.get(0).getEncodedName(), 373 pair.getFirst().getEncodedName()); 374 return regions; 375 } 376 377 private static void testGetRegion(final Connection connection, 378 final RegionInfo region) 379 throws IOException, InterruptedException { 380 Pair<RegionInfo, ServerName> pair = 381 MetaTableAccessor.getRegion(connection, region.getRegionName()); 382 assertEquals(region.getEncodedName(), 383 pair.getFirst().getEncodedName()); 384 } 385 386 @Test 387 public void testParseReplicaIdFromServerColumn() { 388 String column1 = HConstants.SERVER_QUALIFIER_STR; 389 assertEquals(0, 390 MetaTableAccessor.parseReplicaIdFromServerColumn(Bytes.toBytes(column1))); 391 String column2 = column1 + MetaTableAccessor.META_REPLICA_ID_DELIMITER; 392 assertEquals(-1, 393 MetaTableAccessor.parseReplicaIdFromServerColumn(Bytes.toBytes(column2))); 394 String column3 = column2 + "00"; 395 assertEquals(-1, 396 MetaTableAccessor.parseReplicaIdFromServerColumn(Bytes.toBytes(column3))); 397 String column4 = column3 + "2A"; 398 assertEquals(42, 399 MetaTableAccessor.parseReplicaIdFromServerColumn(Bytes.toBytes(column4))); 400 String column5 = column4 + "2A"; 401 assertEquals(-1, 402 MetaTableAccessor.parseReplicaIdFromServerColumn(Bytes.toBytes(column5))); 403 String column6 = HConstants.STARTCODE_QUALIFIER_STR; 404 assertEquals(-1, 405 MetaTableAccessor.parseReplicaIdFromServerColumn(Bytes.toBytes(column6))); 406 } 407 408 /** 409 * The info we can get from the regionName is: table name, start key, regionId, replicaId. 410 */ 411 @Test 412 public void testParseRegionInfoFromRegionName() throws IOException { 413 RegionInfo originalRegionInfo = RegionInfoBuilder.newBuilder( 414 TableName.valueOf(name.getMethodName())).setRegionId(999999L) 415 .setStartKey(Bytes.toBytes("2")).setEndKey(Bytes.toBytes("3")) 416 .setReplicaId(1).build(); 417 RegionInfo newParsedRegionInfo = MetaTableAccessor 418 .parseRegionInfoFromRegionName(originalRegionInfo.getRegionName()); 419 assertEquals("Parse TableName error", originalRegionInfo.getTable(), 420 newParsedRegionInfo.getTable()); 421 assertEquals("Parse regionId error", originalRegionInfo.getRegionId(), 422 newParsedRegionInfo.getRegionId()); 423 assertTrue("Parse startKey error", Bytes.equals(originalRegionInfo.getStartKey(), 424 newParsedRegionInfo.getStartKey())); 425 assertEquals("Parse replicaId error", originalRegionInfo.getReplicaId(), 426 newParsedRegionInfo.getReplicaId()); 427 assertTrue("We can't parse endKey from regionName only", 428 Bytes.equals(HConstants.EMPTY_END_ROW, newParsedRegionInfo.getEndKey())); 429 } 430 431 @Test 432 public void testMetaReaderGetColumnMethods() { 433 assertArrayEquals(HConstants.SERVER_QUALIFIER, MetaTableAccessor.getServerColumn(0)); 434 assertArrayEquals(Bytes.toBytes(HConstants.SERVER_QUALIFIER_STR 435 + MetaTableAccessor.META_REPLICA_ID_DELIMITER + "002A"), 436 MetaTableAccessor.getServerColumn(42)); 437 438 assertArrayEquals(HConstants.STARTCODE_QUALIFIER, 439 MetaTableAccessor.getStartCodeColumn(0)); 440 assertArrayEquals(Bytes.toBytes(HConstants.STARTCODE_QUALIFIER_STR 441 + MetaTableAccessor.META_REPLICA_ID_DELIMITER + "002A"), 442 MetaTableAccessor.getStartCodeColumn(42)); 443 444 assertArrayEquals(HConstants.SEQNUM_QUALIFIER, 445 MetaTableAccessor.getSeqNumColumn(0)); 446 assertArrayEquals(Bytes.toBytes(HConstants.SEQNUM_QUALIFIER_STR 447 + MetaTableAccessor.META_REPLICA_ID_DELIMITER + "002A"), 448 MetaTableAccessor.getSeqNumColumn(42)); 449 } 450 451 @Test 452 public void testMetaLocationsForRegionReplicas() throws IOException { 453 ServerName serverName0 = ServerName.valueOf("foo", 60010, random.nextLong()); 454 ServerName serverName1 = ServerName.valueOf("bar", 60010, random.nextLong()); 455 ServerName serverName100 = ServerName.valueOf("baz", 60010, random.nextLong()); 456 457 long regionId = System.currentTimeMillis(); 458 RegionInfo primary = RegionInfoBuilder.newBuilder(TableName.valueOf(name.getMethodName())) 459 .setStartKey(HConstants.EMPTY_START_ROW) 460 .setEndKey(HConstants.EMPTY_END_ROW) 461 .setSplit(false) 462 .setRegionId(regionId) 463 .setReplicaId(0) 464 .build(); 465 RegionInfo replica1 = RegionInfoBuilder.newBuilder(TableName.valueOf(name.getMethodName())) 466 .setStartKey(HConstants.EMPTY_START_ROW) 467 .setEndKey(HConstants.EMPTY_END_ROW) 468 .setSplit(false) 469 .setRegionId(regionId) 470 .setReplicaId(1) 471 .build(); 472 RegionInfo replica100 = RegionInfoBuilder.newBuilder(TableName.valueOf(name.getMethodName())) 473 .setStartKey(HConstants.EMPTY_START_ROW) 474 .setEndKey(HConstants.EMPTY_END_ROW) 475 .setSplit(false) 476 .setRegionId(regionId) 477 .setReplicaId(100) 478 .build(); 479 480 long seqNum0 = random.nextLong(); 481 long seqNum1 = random.nextLong(); 482 long seqNum100 = random.nextLong(); 483 484 try (Table meta = MetaTableAccessor.getMetaHTable(connection)) { 485 MetaTableAccessor.updateRegionLocation(connection, primary, serverName0, seqNum0, 486 EnvironmentEdgeManager.currentTime()); 487 488 // assert that the server, startcode and seqNum columns are there for the primary region 489 assertMetaLocation(meta, primary.getRegionName(), serverName0, seqNum0, 0, true); 490 491 // add replica = 1 492 MetaTableAccessor.updateRegionLocation(connection, replica1, serverName1, seqNum1, 493 EnvironmentEdgeManager.currentTime()); 494 // check whether the primary is still there 495 assertMetaLocation(meta, primary.getRegionName(), serverName0, seqNum0, 0, true); 496 // now check for replica 1 497 assertMetaLocation(meta, primary.getRegionName(), serverName1, seqNum1, 1, true); 498 499 // add replica = 1 500 MetaTableAccessor.updateRegionLocation(connection, replica100, serverName100, seqNum100, 501 EnvironmentEdgeManager.currentTime()); 502 // check whether the primary is still there 503 assertMetaLocation(meta, primary.getRegionName(), serverName0, seqNum0, 0, true); 504 // check whether the replica 1 is still there 505 assertMetaLocation(meta, primary.getRegionName(), serverName1, seqNum1, 1, true); 506 // now check for replica 1 507 assertMetaLocation(meta, primary.getRegionName(), serverName100, seqNum100, 100, true); 508 } 509 } 510 511 public static void assertMetaLocation(Table meta, byte[] row, ServerName serverName, 512 long seqNum, int replicaId, boolean checkSeqNum) throws IOException { 513 Get get = new Get(row); 514 Result result = meta.get(get); 515 assertTrue(Bytes.equals( 516 result.getValue(HConstants.CATALOG_FAMILY, MetaTableAccessor.getServerColumn(replicaId)), 517 Bytes.toBytes(serverName.getHostAndPort()))); 518 assertTrue(Bytes.equals( 519 result.getValue(HConstants.CATALOG_FAMILY, MetaTableAccessor.getStartCodeColumn(replicaId)), 520 Bytes.toBytes(serverName.getStartcode()))); 521 if (checkSeqNum) { 522 assertTrue(Bytes.equals( 523 result.getValue(HConstants.CATALOG_FAMILY, MetaTableAccessor.getSeqNumColumn(replicaId)), 524 Bytes.toBytes(seqNum))); 525 } 526 } 527 528 public static void assertEmptyMetaLocation(Table meta, byte[] row, int replicaId) 529 throws IOException { 530 Get get = new Get(row); 531 Result result = meta.get(get); 532 Cell serverCell = result.getColumnLatestCell(HConstants.CATALOG_FAMILY, 533 MetaTableAccessor.getServerColumn(replicaId)); 534 Cell startCodeCell = result.getColumnLatestCell(HConstants.CATALOG_FAMILY, 535 MetaTableAccessor.getStartCodeColumn(replicaId)); 536 assertNotNull(serverCell); 537 assertNotNull(startCodeCell); 538 assertEquals(0, serverCell.getValueLength()); 539 assertEquals(0, startCodeCell.getValueLength()); 540 } 541 542 @Test 543 public void testMetaLocationForRegionReplicasIsAddedAtTableCreation() throws IOException { 544 long regionId = System.currentTimeMillis(); 545 RegionInfo primary = RegionInfoBuilder.newBuilder(TableName.valueOf(name.getMethodName())) 546 .setStartKey(HConstants.EMPTY_START_ROW) 547 .setEndKey(HConstants.EMPTY_END_ROW) 548 .setSplit(false) 549 .setRegionId(regionId) 550 .setReplicaId(0) 551 .build(); 552 553 Table meta = MetaTableAccessor.getMetaHTable(connection); 554 try { 555 List<RegionInfo> regionInfos = Lists.newArrayList(primary); 556 MetaTableAccessor.addRegionsToMeta(connection, regionInfos, 3); 557 558 assertEmptyMetaLocation(meta, primary.getRegionName(), 1); 559 assertEmptyMetaLocation(meta, primary.getRegionName(), 2); 560 } finally { 561 meta.close(); 562 } 563 } 564 565 @Test 566 public void testMetaLocationForRegionReplicasIsAddedAtRegionSplit() throws IOException { 567 long regionId = System.currentTimeMillis(); 568 ServerName serverName0 = ServerName.valueOf("foo", 60010, random.nextLong()); 569 RegionInfo parent = RegionInfoBuilder.newBuilder(TableName.valueOf(name.getMethodName())) 570 .setStartKey(HConstants.EMPTY_START_ROW) 571 .setEndKey(HConstants.EMPTY_END_ROW) 572 .setSplit(false) 573 .setRegionId(regionId) 574 .setReplicaId(0) 575 .build(); 576 577 RegionInfo splitA = RegionInfoBuilder.newBuilder(TableName.valueOf(name.getMethodName())) 578 .setStartKey(HConstants.EMPTY_START_ROW) 579 .setEndKey(Bytes.toBytes("a")) 580 .setSplit(false) 581 .setRegionId(regionId + 1) 582 .setReplicaId(0) 583 .build(); 584 RegionInfo splitB = RegionInfoBuilder.newBuilder(TableName.valueOf(name.getMethodName())) 585 .setStartKey(Bytes.toBytes("a")) 586 .setEndKey(HConstants.EMPTY_END_ROW) 587 .setSplit(false) 588 .setRegionId(regionId + 1) 589 .setReplicaId(0) 590 .build(); 591 592 try (Table meta = MetaTableAccessor.getMetaHTable(connection)) { 593 List<RegionInfo> regionInfos = Lists.newArrayList(parent); 594 MetaTableAccessor.addRegionsToMeta(connection, regionInfos, 3); 595 596 MetaTableAccessor.splitRegion(connection, parent, -1L, splitA, splitB, serverName0, 3); 597 598 assertEmptyMetaLocation(meta, splitA.getRegionName(), 1); 599 assertEmptyMetaLocation(meta, splitA.getRegionName(), 2); 600 assertEmptyMetaLocation(meta, splitB.getRegionName(), 1); 601 assertEmptyMetaLocation(meta, splitB.getRegionName(), 2); 602 } 603 } 604 605 @Test 606 public void testMetaLocationForRegionReplicasIsAddedAtRegionMerge() throws IOException { 607 long regionId = System.currentTimeMillis(); 608 ServerName serverName0 = ServerName.valueOf("foo", 60010, random.nextLong()); 609 610 RegionInfo parentA = RegionInfoBuilder.newBuilder(TableName.valueOf(name.getMethodName())) 611 .setStartKey(Bytes.toBytes("a")) 612 .setEndKey(HConstants.EMPTY_END_ROW) 613 .setSplit(false) 614 .setRegionId(regionId) 615 .setReplicaId(0) 616 .build(); 617 618 RegionInfo parentB = RegionInfoBuilder.newBuilder(TableName.valueOf(name.getMethodName())) 619 .setStartKey(HConstants.EMPTY_START_ROW) 620 .setEndKey(Bytes.toBytes("a")) 621 .setSplit(false) 622 .setRegionId(regionId) 623 .setReplicaId(0) 624 .build(); 625 RegionInfo merged = RegionInfoBuilder.newBuilder(TableName.valueOf(name.getMethodName())) 626 .setStartKey(HConstants.EMPTY_START_ROW) 627 .setEndKey(HConstants.EMPTY_END_ROW) 628 .setSplit(false) 629 .setRegionId(regionId + 1) 630 .setReplicaId(0) 631 .build(); 632 633 try (Table meta = MetaTableAccessor.getMetaHTable(connection)) { 634 List<RegionInfo> regionInfos = Lists.newArrayList(parentA, parentB); 635 MetaTableAccessor.addRegionsToMeta(connection, regionInfos, 3); 636 MetaTableAccessor.mergeRegions(connection, merged, getMapOfRegionsToSeqNum(parentA, parentB), 637 serverName0, 3); 638 assertEmptyMetaLocation(meta, merged.getRegionName(), 1); 639 assertEmptyMetaLocation(meta, merged.getRegionName(), 2); 640 } 641 } 642 643 private Map<RegionInfo, Long> getMapOfRegionsToSeqNum(RegionInfo ... regions) { 644 Map<RegionInfo, Long> mids = new HashMap<>(regions.length); 645 for (RegionInfo region: regions) { 646 mids.put(region, -1L); 647 } 648 return mids; 649 } 650 651 @Test 652 public void testMetaScanner() throws Exception { 653 LOG.info("Starting " + name.getMethodName()); 654 655 final TableName tableName = TableName.valueOf(name.getMethodName()); 656 final byte[] FAMILY = Bytes.toBytes("family"); 657 final byte[][] SPLIT_KEYS = 658 new byte[][] { Bytes.toBytes("region_a"), Bytes.toBytes("region_b") }; 659 660 UTIL.createTable(tableName, FAMILY, SPLIT_KEYS); 661 Table table = connection.getTable(tableName); 662 // Make sure all the regions are deployed 663 UTIL.countRows(table); 664 665 MetaTableAccessor.Visitor visitor = 666 mock(MetaTableAccessor.Visitor.class); 667 doReturn(true).when(visitor).visit((Result) anyObject()); 668 669 // Scanning the entire table should give us three rows 670 MetaTableAccessor.scanMetaForTableRegions(connection, visitor, tableName); 671 verify(visitor, times(3)).visit((Result) anyObject()); 672 673 // Scanning the table with a specified empty start row should also 674 // give us three hbase:meta rows 675 reset(visitor); 676 doReturn(true).when(visitor).visit((Result) anyObject()); 677 MetaTableAccessor.scanMeta(connection, visitor, tableName, null, 1000); 678 verify(visitor, times(3)).visit((Result) anyObject()); 679 680 // Scanning the table starting in the middle should give us two rows: 681 // region_a and region_b 682 reset(visitor); 683 doReturn(true).when(visitor).visit((Result) anyObject()); 684 MetaTableAccessor.scanMeta(connection, visitor, tableName, Bytes.toBytes("region_ac"), 1000); 685 verify(visitor, times(2)).visit((Result) anyObject()); 686 687 // Scanning with a limit of 1 should only give us one row 688 reset(visitor); 689 doReturn(true).when(visitor).visit((Result) anyObject()); 690 MetaTableAccessor.scanMeta(connection, visitor, tableName, Bytes.toBytes("region_ac"), 1); 691 verify(visitor, times(1)).visit((Result) anyObject()); 692 table.close(); 693 } 694 695 /** 696 * Tests whether maximum of masters system time versus RSs local system time is used 697 */ 698 @Test 699 public void testMastersSystemTimeIsUsedInUpdateLocations() throws IOException { 700 long regionId = System.currentTimeMillis(); 701 RegionInfo regionInfo = RegionInfoBuilder.newBuilder(TableName.valueOf(name.getMethodName())) 702 .setStartKey(HConstants.EMPTY_START_ROW) 703 .setEndKey(HConstants.EMPTY_END_ROW) 704 .setSplit(false) 705 .setRegionId(regionId) 706 .setReplicaId(0) 707 .build(); 708 709 ServerName sn = ServerName.valueOf("bar", 0, 0); 710 try (Table meta = MetaTableAccessor.getMetaHTable(connection)) { 711 List<RegionInfo> regionInfos = Lists.newArrayList(regionInfo); 712 MetaTableAccessor.addRegionsToMeta(connection, regionInfos, 1); 713 714 long masterSystemTime = EnvironmentEdgeManager.currentTime() + 123456789; 715 MetaTableAccessor.updateRegionLocation(connection, regionInfo, sn, 1, masterSystemTime); 716 717 Get get = new Get(regionInfo.getRegionName()); 718 Result result = meta.get(get); 719 Cell serverCell = result.getColumnLatestCell(HConstants.CATALOG_FAMILY, 720 MetaTableAccessor.getServerColumn(0)); 721 Cell startCodeCell = result.getColumnLatestCell(HConstants.CATALOG_FAMILY, 722 MetaTableAccessor.getStartCodeColumn(0)); 723 Cell seqNumCell = result.getColumnLatestCell(HConstants.CATALOG_FAMILY, 724 MetaTableAccessor.getSeqNumColumn(0)); 725 assertNotNull(serverCell); 726 assertNotNull(startCodeCell); 727 assertNotNull(seqNumCell); 728 assertTrue(serverCell.getValueLength() > 0); 729 assertTrue(startCodeCell.getValueLength() > 0); 730 assertTrue(seqNumCell.getValueLength() > 0); 731 assertEquals(masterSystemTime, serverCell.getTimestamp()); 732 assertEquals(masterSystemTime, startCodeCell.getTimestamp()); 733 assertEquals(masterSystemTime, seqNumCell.getTimestamp()); 734 } 735 } 736 737 @Test 738 public void testMastersSystemTimeIsUsedInMergeRegions() throws IOException { 739 long regionId = System.currentTimeMillis(); 740 741 RegionInfo regionInfoA = RegionInfoBuilder.newBuilder(TableName.valueOf(name.getMethodName())) 742 .setStartKey(HConstants.EMPTY_START_ROW) 743 .setEndKey(new byte[] {'a'}) 744 .setSplit(false) 745 .setRegionId(regionId) 746 .setReplicaId(0) 747 .build(); 748 749 RegionInfo regionInfoB = RegionInfoBuilder.newBuilder(TableName.valueOf(name.getMethodName())) 750 .setStartKey(new byte[] {'a'}) 751 .setEndKey(HConstants.EMPTY_END_ROW) 752 .setSplit(false) 753 .setRegionId(regionId) 754 .setReplicaId(0) 755 .build(); 756 RegionInfo mergedRegionInfo = RegionInfoBuilder.newBuilder(TableName.valueOf(name.getMethodName())) 757 .setStartKey(HConstants.EMPTY_START_ROW) 758 .setEndKey(HConstants.EMPTY_END_ROW) 759 .setSplit(false) 760 .setRegionId(regionId) 761 .setReplicaId(0) 762 .build(); 763 764 ServerName sn = ServerName.valueOf("bar", 0, 0); 765 try (Table meta = MetaTableAccessor.getMetaHTable(connection)) { 766 List<RegionInfo> regionInfos = Lists.newArrayList(regionInfoA, regionInfoB); 767 MetaTableAccessor.addRegionsToMeta(connection, regionInfos, 1); 768 769 // write the serverName column with a big current time, but set the masters time as even 770 // bigger. When region merge deletes the rows for regionA and regionB, the serverName columns 771 // should not be seen by the following get 772 long serverNameTime = EnvironmentEdgeManager.currentTime() + 100000000; 773 long masterSystemTime = EnvironmentEdgeManager.currentTime() + 123456789; 774 775 // write the serverName columns 776 MetaTableAccessor.updateRegionLocation(connection, regionInfoA, sn, 1, serverNameTime); 777 778 // assert that we have the serverName column with expected ts 779 Get get = new Get(mergedRegionInfo.getRegionName()); 780 Result result = meta.get(get); 781 Cell serverCell = result.getColumnLatestCell(HConstants.CATALOG_FAMILY, 782 MetaTableAccessor.getServerColumn(0)); 783 assertNotNull(serverCell); 784 assertEquals(serverNameTime, serverCell.getTimestamp()); 785 786 ManualEnvironmentEdge edge = new ManualEnvironmentEdge(); 787 edge.setValue(masterSystemTime); 788 EnvironmentEdgeManager.injectEdge(edge); 789 try { 790 // now merge the regions, effectively deleting the rows for region a and b. 791 MetaTableAccessor.mergeRegions(connection, mergedRegionInfo, 792 getMapOfRegionsToSeqNum(regionInfoA, regionInfoB), sn, 1); 793 } finally { 794 EnvironmentEdgeManager.reset(); 795 } 796 797 798 result = meta.get(get); 799 serverCell = result.getColumnLatestCell(HConstants.CATALOG_FAMILY, 800 MetaTableAccessor.getServerColumn(0)); 801 Cell startCodeCell = result.getColumnLatestCell(HConstants.CATALOG_FAMILY, 802 MetaTableAccessor.getStartCodeColumn(0)); 803 Cell seqNumCell = result.getColumnLatestCell(HConstants.CATALOG_FAMILY, 804 MetaTableAccessor.getSeqNumColumn(0)); 805 assertNull(serverCell); 806 assertNull(startCodeCell); 807 assertNull(seqNumCell); 808 } 809 } 810 811 public static class SpyingRpcSchedulerFactory extends SimpleRpcSchedulerFactory { 812 @Override 813 public RpcScheduler create(Configuration conf, PriorityFunction priority, Abortable server) { 814 final RpcScheduler delegate = super.create(conf, priority, server); 815 return new SpyingRpcScheduler(delegate); 816 } 817 } 818 819 public static class SpyingRpcScheduler extends DelegatingRpcScheduler { 820 long numPriorityCalls = 0; 821 822 public SpyingRpcScheduler(RpcScheduler delegate) { 823 super(delegate); 824 } 825 826 @Override 827 public boolean dispatch(CallRunner task) throws IOException, InterruptedException { 828 int priority = task.getRpcCall().getPriority(); 829 830 if (priority > HConstants.QOS_THRESHOLD) { 831 numPriorityCalls++; 832 } 833 return super.dispatch(task); 834 } 835 } 836 837 @Test 838 public void testMetaUpdatesGoToPriorityQueue() throws Exception { 839 // This test has to be end-to-end, and do the verification from the server side 840 Configuration c = UTIL.getConfiguration(); 841 842 c.set(RSRpcServices.REGION_SERVER_RPC_SCHEDULER_FACTORY_CLASS, 843 SpyingRpcSchedulerFactory.class.getName()); 844 845 // restart so that new config takes place 846 afterClass(); 847 beforeClass(); 848 849 final TableName tableName = TableName.valueOf(name.getMethodName()); 850 try (Admin admin = connection.getAdmin(); 851 RegionLocator rl = connection.getRegionLocator(tableName)) { 852 853 // create a table and prepare for a manual split 854 UTIL.createTable(tableName, "cf1"); 855 856 HRegionLocation loc = rl.getAllRegionLocations().get(0); 857 RegionInfo parent = loc.getRegionInfo(); 858 long rid = 1000; 859 byte[] splitKey = Bytes.toBytes("a"); 860 RegionInfo splitA = RegionInfoBuilder.newBuilder(parent.getTable()) 861 .setStartKey(parent.getStartKey()) 862 .setEndKey(splitKey) 863 .setSplit(false) 864 .setRegionId(rid) 865 .build(); 866 RegionInfo splitB = RegionInfoBuilder.newBuilder(parent.getTable()) 867 .setStartKey(splitKey) 868 .setEndKey(parent.getEndKey()) 869 .setSplit(false) 870 .setRegionId(rid) 871 .build(); 872 873 // find the meta server 874 MiniHBaseCluster cluster = UTIL.getMiniHBaseCluster(); 875 int rsIndex = cluster.getServerWithMeta(); 876 HRegionServer rs; 877 if (rsIndex >= 0) { 878 rs = cluster.getRegionServer(rsIndex); 879 } else { 880 // it is in master 881 rs = cluster.getMaster(); 882 } 883 SpyingRpcScheduler scheduler = (SpyingRpcScheduler) rs.getRpcServer().getScheduler(); 884 long prevCalls = scheduler.numPriorityCalls; 885 MetaTableAccessor.splitRegion(connection, parent, -1L, splitA, splitB, loc.getServerName(), 886 1); 887 888 assertTrue(prevCalls < scheduler.numPriorityCalls); 889 } 890 } 891 892 @Test 893 public void testEmptyMetaDaughterLocationDuringSplit() throws IOException { 894 long regionId = System.currentTimeMillis(); 895 ServerName serverName0 = ServerName.valueOf("foo", 60010, random.nextLong()); 896 RegionInfo parent = RegionInfoBuilder.newBuilder(TableName.valueOf("table_foo")) 897 .setStartKey(HConstants.EMPTY_START_ROW) 898 .setEndKey(HConstants.EMPTY_END_ROW) 899 .setSplit(false) 900 .setRegionId(regionId) 901 .setReplicaId(0) 902 .build(); 903 RegionInfo splitA = RegionInfoBuilder.newBuilder(TableName.valueOf("table_foo")) 904 .setStartKey(HConstants.EMPTY_START_ROW) 905 .setEndKey(Bytes.toBytes("a")) 906 .setSplit(false) 907 .setRegionId(regionId + 1) 908 .setReplicaId(0) 909 .build(); 910 RegionInfo splitB = RegionInfoBuilder.newBuilder(TableName.valueOf("table_foo")) 911 .setStartKey(Bytes.toBytes("a")) 912 .setEndKey(HConstants.EMPTY_END_ROW) 913 .setSplit(false) 914 .setRegionId(regionId + 1) 915 .setReplicaId(0) 916 .build(); 917 918 Table meta = MetaTableAccessor.getMetaHTable(connection); 919 try { 920 List<RegionInfo> regionInfos = Lists.newArrayList(parent); 921 MetaTableAccessor.addRegionsToMeta(connection, regionInfos, 3); 922 923 MetaTableAccessor.splitRegion(connection, parent, -1L, splitA, splitB, 924 serverName0, 3); 925 Get get1 = new Get(splitA.getRegionName()); 926 Result resultA = meta.get(get1); 927 Cell serverCellA = resultA.getColumnLatestCell(HConstants.CATALOG_FAMILY, 928 MetaTableAccessor.getServerColumn(splitA.getReplicaId())); 929 Cell startCodeCellA = resultA.getColumnLatestCell(HConstants.CATALOG_FAMILY, 930 MetaTableAccessor.getStartCodeColumn(splitA.getReplicaId())); 931 assertNull(serverCellA); 932 assertNull(startCodeCellA); 933 934 Get get2 = new Get(splitA.getRegionName()); 935 Result resultB = meta.get(get2); 936 Cell serverCellB = resultB.getColumnLatestCell(HConstants.CATALOG_FAMILY, 937 MetaTableAccessor.getServerColumn(splitB.getReplicaId())); 938 Cell startCodeCellB = resultB.getColumnLatestCell(HConstants.CATALOG_FAMILY, 939 MetaTableAccessor.getStartCodeColumn(splitB.getReplicaId())); 940 assertNull(serverCellB); 941 assertNull(startCodeCellB); 942 } finally { 943 if (meta != null) { 944 meta.close(); 945 } 946 } 947 } 948 949 @Test 950 public void testScanByRegionEncodedNameExistingRegion() throws Exception { 951 final TableName tableName = TableName.valueOf("testScanByRegionEncodedNameExistingRegion"); 952 UTIL.createTable(tableName, "cf"); 953 final List<HRegion> regions = UTIL.getHBaseCluster().getRegions(tableName); 954 final String encodedName = regions.get(0).getRegionInfo().getEncodedName(); 955 final Result result = MetaTableAccessor.scanByRegionEncodedName(UTIL.getConnection(), 956 encodedName); 957 assertNotNull(result); 958 assertTrue(result.advance()); 959 final String resultingRowKey = CellUtil.getCellKeyAsString(result.current()); 960 assertTrue(resultingRowKey.contains(encodedName)); 961 UTIL.deleteTable(tableName); 962 } 963 964 @Test 965 public void testScanByRegionEncodedNameNonExistingRegion() throws Exception { 966 final String encodedName = "nonexistingregion"; 967 final Result result = MetaTableAccessor.scanByRegionEncodedName(UTIL.getConnection(), 968 encodedName); 969 assertNull(result); 970 } 971} 972