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