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 */ 018 019package org.apache.hadoop.hbase.master; 020 021import edu.umd.cs.findbugs.annotations.Nullable; 022import java.io.UnsupportedEncodingException; 023import java.nio.charset.StandardCharsets; 024import java.util.HashMap; 025import java.util.List; 026import java.util.Map; 027import java.util.Set; 028 029import org.apache.hadoop.conf.Configuration; 030import org.apache.hadoop.hbase.ClusterMetrics; 031import org.apache.hadoop.hbase.HBaseClassTestRule; 032import org.apache.hadoop.hbase.HBaseTestingUtility; 033import org.apache.hadoop.hbase.RegionMetrics; 034import org.apache.hadoop.hbase.ServerMetrics; 035import org.apache.hadoop.hbase.ServerName; 036import org.apache.hadoop.hbase.Size; 037import org.apache.hadoop.hbase.Stoppable; 038import org.apache.hadoop.hbase.TableName; 039import org.apache.hadoop.hbase.UserMetrics; 040import org.apache.hadoop.hbase.client.CompactionState; 041import org.apache.hadoop.hbase.client.RegionInfo; 042import org.apache.hadoop.hbase.client.RegionStatesCount; 043import org.apache.hadoop.hbase.master.assignment.AssignmentManager; 044import org.apache.hadoop.hbase.replication.ReplicationLoadSink; 045import org.apache.hadoop.hbase.replication.ReplicationLoadSource; 046import org.apache.hadoop.hbase.testclassification.MasterTests; 047import org.apache.hadoop.hbase.testclassification.SmallTests; 048import org.apache.hadoop.hbase.util.Bytes; 049import org.junit.After; 050import org.junit.Before; 051import org.junit.ClassRule; 052import org.junit.Test; 053import org.junit.experimental.categories.Category; 054import org.mockito.Mockito; 055import org.slf4j.Logger; 056import org.slf4j.LoggerFactory; 057 058/** 059 * Test for RegionsRecoveryChore 060 */ 061@Category({MasterTests.class, SmallTests.class}) 062public class TestRegionsRecoveryChore { 063 064 @ClassRule 065 public static final HBaseClassTestRule CLASS_RULE = 066 HBaseClassTestRule.forClass(TestRegionsRecoveryChore.class); 067 068 private static final Logger LOG = LoggerFactory.getLogger(TestRegionsRecoveryChore.class); 069 070 private static final HBaseTestingUtility HBASE_TESTING_UTILITY = new HBaseTestingUtility(); 071 072 private static final String UTF_8_CHARSET = StandardCharsets.UTF_8.name(); 073 074 private HMaster hMaster; 075 076 private AssignmentManager assignmentManager; 077 078 private RegionsRecoveryChore regionsRecoveryChore; 079 080 private static int regionNo; 081 public static final byte[][] REGION_NAME_LIST = new byte[][]{ 082 new byte[]{114, 101, 103, 105, 111, 110, 50, 49, 95, 51}, 083 new byte[]{114, 101, 103, 105, 111, 110, 50, 53, 95, 51}, 084 new byte[]{114, 101, 103, 105, 111, 110, 50, 54, 95, 52}, 085 new byte[]{114, 101, 103, 105, 111, 110, 51, 50, 95, 53}, 086 new byte[]{114, 101, 103, 105, 111, 110, 51, 49, 95, 52}, 087 new byte[]{114, 101, 103, 105, 111, 110, 51, 48, 95, 51}, 088 new byte[]{114, 101, 103, 105, 111, 110, 50, 48, 95, 50}, 089 new byte[]{114, 101, 103, 105, 111, 110, 50, 52, 95, 50}, 090 new byte[]{114, 101, 103, 105, 111, 110, 50, 57, 95, 50}, 091 new byte[]{114, 101, 103, 105, 111, 110, 51, 53, 95, 50}, 092 new byte[]{114, 101, 103, 105, 111, 110, 49, 48, 56, 95, 49, 49} 093 }; 094 095 private Configuration getCustomConf() { 096 Configuration conf = HBASE_TESTING_UTILITY.getConfiguration(); 097 conf.setInt("hbase.master.regions.recovery.check.interval", 100); 098 return conf; 099 } 100 101 @Before 102 public void setUp() throws Exception { 103 this.hMaster = Mockito.mock(HMaster.class); 104 this.assignmentManager = Mockito.mock(AssignmentManager.class); 105 } 106 107 @After 108 public void tearDown() throws Exception { 109 Mockito.verifyNoMoreInteractions(this.hMaster); 110 Mockito.verifyNoMoreInteractions(this.assignmentManager); 111 } 112 113 @Test 114 public void testRegionReopensWithStoreRefConfig() throws Exception { 115 regionNo = 0; 116 ClusterMetrics clusterMetrics = TestRegionsRecoveryChore.getClusterMetrics(4); 117 final Map<ServerName, ServerMetrics> serverMetricsMap = 118 clusterMetrics.getLiveServerMetrics(); 119 LOG.debug("All Region Names with refCount...."); 120 for (ServerMetrics serverMetrics : serverMetricsMap.values()) { 121 Map<byte[], RegionMetrics> regionMetricsMap = serverMetrics.getRegionMetrics(); 122 for (RegionMetrics regionMetrics : regionMetricsMap.values()) { 123 LOG.debug("name: " + new String(regionMetrics.getRegionName()) + " refCount: " + 124 regionMetrics.getStoreRefCount()); 125 } 126 } 127 Mockito.when(hMaster.getClusterMetrics()).thenReturn(clusterMetrics); 128 Mockito.when(hMaster.getAssignmentManager()).thenReturn(assignmentManager); 129 for (byte[] regionName : REGION_NAME_LIST) { 130 Mockito.when(assignmentManager.getRegionInfo(regionName)) 131 .thenReturn(TestRegionsRecoveryChore.getRegionInfo(regionName)); 132 } 133 Stoppable stoppable = new StoppableImplementation(); 134 Configuration configuration = getCustomConf(); 135 configuration.setInt("hbase.regions.recovery.store.file.ref.count", 300); 136 regionsRecoveryChore = new RegionsRecoveryChore(stoppable, configuration, hMaster); 137 regionsRecoveryChore.chore(); 138 139 // Verify that we need to reopen regions of 2 tables 140 Mockito.verify(hMaster, Mockito.times(2)).reopenRegions(Mockito.any(), Mockito.anyList(), 141 Mockito.anyLong(), Mockito.anyLong()); 142 Mockito.verify(hMaster, Mockito.times(1)).getClusterMetrics(); 143 144 // Verify that we need to reopen total 3 regions that have refCount > 300 145 Mockito.verify(hMaster, Mockito.times(3)).getAssignmentManager(); 146 Mockito.verify(assignmentManager, Mockito.times(3)) 147 .getRegionInfo(Mockito.any()); 148 } 149 150 @Test 151 public void testRegionReopensWithLessThreshold() throws Exception { 152 regionNo = 0; 153 ClusterMetrics clusterMetrics = TestRegionsRecoveryChore.getClusterMetrics(4); 154 final Map<ServerName, ServerMetrics> serverMetricsMap = 155 clusterMetrics.getLiveServerMetrics(); 156 LOG.debug("All Region Names with refCount...."); 157 for (ServerMetrics serverMetrics : serverMetricsMap.values()) { 158 Map<byte[], RegionMetrics> regionMetricsMap = serverMetrics.getRegionMetrics(); 159 for (RegionMetrics regionMetrics : regionMetricsMap.values()) { 160 LOG.debug("name: " + new String(regionMetrics.getRegionName()) + " refCount: " + 161 regionMetrics.getStoreRefCount()); 162 } 163 } 164 Mockito.when(hMaster.getClusterMetrics()).thenReturn(clusterMetrics); 165 Mockito.when(hMaster.getAssignmentManager()).thenReturn(assignmentManager); 166 for (byte[] regionName : REGION_NAME_LIST) { 167 Mockito.when(assignmentManager.getRegionInfo(regionName)) 168 .thenReturn(TestRegionsRecoveryChore.getRegionInfo(regionName)); 169 } 170 Stoppable stoppable = new StoppableImplementation(); 171 Configuration configuration = getCustomConf(); 172 configuration.setInt("hbase.regions.recovery.store.file.ref.count", 400); 173 regionsRecoveryChore = new RegionsRecoveryChore(stoppable, configuration, hMaster); 174 regionsRecoveryChore.chore(); 175 176 // Verify that we need to reopen regions of only 1 table 177 Mockito.verify(hMaster, Mockito.times(1)).reopenRegions(Mockito.any(), Mockito.anyList(), 178 Mockito.anyLong(), Mockito.anyLong()); 179 Mockito.verify(hMaster, Mockito.times(1)).getClusterMetrics(); 180 181 // Verify that we need to reopen only 1 region with refCount > 400 182 Mockito.verify(hMaster, Mockito.times(1)).getAssignmentManager(); 183 Mockito.verify(assignmentManager, Mockito.times(1)) 184 .getRegionInfo(Mockito.any()); 185 } 186 187 @Test 188 public void testRegionReopensWithoutStoreRefConfig() throws Exception { 189 regionNo = 0; 190 ClusterMetrics clusterMetrics = TestRegionsRecoveryChore.getClusterMetrics(10); 191 final Map<ServerName, ServerMetrics> serverMetricsMap = 192 clusterMetrics.getLiveServerMetrics(); 193 LOG.debug("All Region Names with refCount...."); 194 for (ServerMetrics serverMetrics : serverMetricsMap.values()) { 195 Map<byte[], RegionMetrics> regionMetricsMap = serverMetrics.getRegionMetrics(); 196 for (RegionMetrics regionMetrics : regionMetricsMap.values()) { 197 LOG.debug("name: " + new String(regionMetrics.getRegionName()) + " refCount: " + 198 regionMetrics.getStoreRefCount()); 199 } 200 } 201 Mockito.when(hMaster.getClusterMetrics()).thenReturn(clusterMetrics); 202 Mockito.when(hMaster.getAssignmentManager()).thenReturn(assignmentManager); 203 for (byte[] regionName : REGION_NAME_LIST) { 204 Mockito.when(assignmentManager.getRegionInfo(regionName)) 205 .thenReturn(TestRegionsRecoveryChore.getRegionInfo(regionName)); 206 } 207 Stoppable stoppable = new StoppableImplementation(); 208 Configuration configuration = getCustomConf(); 209 configuration.unset("hbase.regions.recovery.store.file.ref.count"); 210 regionsRecoveryChore = new RegionsRecoveryChore(stoppable, configuration, hMaster); 211 regionsRecoveryChore.chore(); 212 213 // Verify that by default the feature is turned off so no regions 214 // should be reopened 215 Mockito.verify(hMaster, Mockito.times(0)).reopenRegions(Mockito.any(), Mockito.anyList(), 216 Mockito.anyLong(), Mockito.anyLong()); 217 218 // default maxCompactedStoreFileRefCount is -1 (no regions to be reopened using AM) 219 Mockito.verify(hMaster, Mockito.times(0)).getAssignmentManager(); 220 Mockito.verify(assignmentManager, Mockito.times(0)) 221 .getRegionInfo(Mockito.any()); 222 } 223 224 private static ClusterMetrics getClusterMetrics(int noOfLiveServer) { 225 ClusterMetrics clusterMetrics = new ClusterMetrics() { 226 227 @Nullable 228 @Override 229 public String getHBaseVersion() { 230 return null; 231 } 232 233 @Override 234 public List<ServerName> getDeadServerNames() { 235 return null; 236 } 237 238 @Override 239 public Map<ServerName, ServerMetrics> getLiveServerMetrics() { 240 Map<ServerName, ServerMetrics> liveServerMetrics = new HashMap<>(); 241 for (int i = 0; i < noOfLiveServer; i++) { 242 ServerName serverName = ServerName.valueOf("rs_" + i, 16010, 12345); 243 liveServerMetrics.put(serverName, TestRegionsRecoveryChore.getServerMetrics(i + 3)); 244 } 245 return liveServerMetrics; 246 } 247 248 @Nullable 249 @Override 250 public ServerName getMasterName() { 251 return null; 252 } 253 254 @Override 255 public List<ServerName> getBackupMasterNames() { 256 return null; 257 } 258 259 @Override 260 public List<RegionState> getRegionStatesInTransition() { 261 return null; 262 } 263 264 @Nullable 265 @Override 266 public String getClusterId() { 267 return null; 268 } 269 270 @Override 271 public List<String> getMasterCoprocessorNames() { 272 return null; 273 } 274 275 @Nullable 276 @Override 277 public Boolean getBalancerOn() { 278 return null; 279 } 280 281 @Override 282 public int getMasterInfoPort() { 283 return 0; 284 } 285 286 @Override 287 public List<ServerName> getServersName() { 288 return null; 289 } 290 291 @Override 292 public Map<TableName, RegionStatesCount> getTableRegionStatesCount() { 293 return null; 294 } 295 296 }; 297 return clusterMetrics; 298 } 299 300 private static ServerMetrics getServerMetrics(int noOfRegions) { 301 ServerMetrics serverMetrics = new ServerMetrics() { 302 303 @Override 304 public ServerName getServerName() { 305 return null; 306 } 307 308 @Override 309 public long getRequestCountPerSecond() { 310 return 0; 311 } 312 313 @Override 314 public long getRequestCount() { 315 return 0; 316 } 317 318 @Override 319 public Size getUsedHeapSize() { 320 return null; 321 } 322 323 @Override 324 public Size getMaxHeapSize() { 325 return null; 326 } 327 328 @Override 329 public int getInfoServerPort() { 330 return 0; 331 } 332 333 @Override 334 public List<ReplicationLoadSource> getReplicationLoadSourceList() { 335 return null; 336 } 337 338 @Override 339 public Map<String, List<ReplicationLoadSource>> getReplicationLoadSourceMap() { 340 return null; 341 } 342 343 @Nullable 344 @Override 345 public ReplicationLoadSink getReplicationLoadSink() { 346 return null; 347 } 348 349 @Override 350 public Map<byte[], RegionMetrics> getRegionMetrics() { 351 Map<byte[], RegionMetrics> regionMetricsMap = new HashMap<>(); 352 for (int i = 0; i < noOfRegions; i++) { 353 byte[] regionName = Bytes.toBytes("region" + regionNo + "_" + i); 354 regionMetricsMap.put(regionName, 355 TestRegionsRecoveryChore.getRegionMetrics(regionName, 100 * i)); 356 ++regionNo; 357 } 358 return regionMetricsMap; 359 } 360 361 @Override public Map<byte[], UserMetrics> getUserMetrics() { 362 return new HashMap<>(); 363 } 364 365 @Override 366 public Set<String> getCoprocessorNames() { 367 return null; 368 } 369 370 @Override 371 public long getReportTimestamp() { 372 return 0; 373 } 374 375 @Override 376 public long getLastReportTimestamp() { 377 return 0; 378 } 379 380 }; 381 return serverMetrics; 382 } 383 384 private static RegionMetrics getRegionMetrics(byte[] regionName, int compactedStoreRefCount) { 385 RegionMetrics regionMetrics = new RegionMetrics() { 386 387 @Override 388 public byte[] getRegionName() { 389 return regionName; 390 } 391 392 @Override 393 public int getStoreCount() { 394 return 0; 395 } 396 397 @Override 398 public int getStoreFileCount() { 399 return 0; 400 } 401 402 @Override 403 public Size getStoreFileSize() { 404 return null; 405 } 406 407 @Override 408 public Size getMemStoreSize() { 409 return null; 410 } 411 412 @Override 413 public long getReadRequestCount() { 414 return 0; 415 } 416 417 @Override 418 public long getWriteRequestCount() { 419 return 0; 420 } 421 422 @Override 423 public long getFilteredReadRequestCount() { 424 return 0; 425 } 426 427 @Override 428 public Size getStoreFileIndexSize() { 429 return null; 430 } 431 432 @Override 433 public Size getStoreFileRootLevelIndexSize() { 434 return null; 435 } 436 437 @Override 438 public Size getStoreFileUncompressedDataIndexSize() { 439 return null; 440 } 441 442 @Override 443 public Size getBloomFilterSize() { 444 return null; 445 } 446 447 @Override 448 public long getCompactingCellCount() { 449 return 0; 450 } 451 452 @Override 453 public long getCompactedCellCount() { 454 return 0; 455 } 456 457 @Override 458 public long getCompletedSequenceId() { 459 return 0; 460 } 461 462 @Override 463 public Map<byte[], Long> getStoreSequenceId() { 464 return null; 465 } 466 467 @Override 468 public Size getUncompressedStoreFileSize() { 469 return null; 470 } 471 472 @Override 473 public float getDataLocality() { 474 return 0; 475 } 476 477 @Override 478 public long getLastMajorCompactionTimestamp() { 479 return 0; 480 } 481 482 @Override 483 public int getStoreRefCount() { 484 return compactedStoreRefCount; 485 } 486 487 @Override 488 public int getMaxCompactedStoreFileRefCount() { 489 return compactedStoreRefCount; 490 } 491 492 @Override 493 public float getDataLocalityForSsd() { 494 return 0; 495 } 496 497 @Override 498 public long getBlocksLocalWeight() { 499 return 0; 500 } 501 502 @Override 503 public long getBlocksLocalWithSsdWeight() { 504 return 0; 505 } 506 507 @Override 508 public long getBlocksTotalWeight() { 509 return 0; 510 } 511 512 @Override 513 public CompactionState getCompactionState() { 514 return null; 515 } 516 }; 517 return regionMetrics; 518 } 519 520 private static RegionInfo getRegionInfo(byte[] regionNameBytes) { 521 RegionInfo regionInfo = new RegionInfo() { 522 523 @Override 524 public String getShortNameToLog() { 525 return null; 526 } 527 528 @Override 529 public long getRegionId() { 530 return 0; 531 } 532 533 @Override 534 public byte[] getRegionName() { 535 return new byte[0]; 536 } 537 538 @Override 539 public String getRegionNameAsString() { 540 try { 541 return new String(regionNameBytes, UTF_8_CHARSET); 542 } catch (UnsupportedEncodingException e) { 543 return ""; 544 } 545 } 546 547 @Override 548 public String getEncodedName() { 549 return null; 550 } 551 552 @Override 553 public byte[] getEncodedNameAsBytes() { 554 return new byte[0]; 555 } 556 557 @Override 558 public byte[] getStartKey() { 559 return new byte[0]; 560 } 561 562 @Override 563 public byte[] getEndKey() { 564 return new byte[0]; 565 } 566 567 @Override 568 public TableName getTable() { 569 String regionName; 570 try { 571 regionName = new String(regionNameBytes, UTF_8_CHARSET); 572 } catch (UnsupportedEncodingException e) { 573 regionName = ""; 574 } 575 int regionNo = Integer.parseInt(regionName.split("_")[1]); 576 TableName tableName = TableName.valueOf("table_" + regionNo % 3); 577 return tableName; 578 } 579 580 @Override 581 public int getReplicaId() { 582 return 0; 583 } 584 585 @Override 586 public boolean isSplit() { 587 return false; 588 } 589 590 @Override 591 public boolean isOffline() { 592 return false; 593 } 594 595 @Override 596 public boolean isSplitParent() { 597 return false; 598 } 599 600 @Override 601 public boolean isMetaRegion() { 602 return false; 603 } 604 605 @Override 606 public boolean containsRange(byte[] rangeStartKey, byte[] rangeEndKey) { 607 return false; 608 } 609 610 @Override 611 public boolean containsRow(byte[] row) { 612 return false; 613 } 614 615 }; 616 return regionInfo; 617 } 618 619 /** 620 * Simple helper class that just keeps track of whether or not its stopped. 621 */ 622 private static class StoppableImplementation implements Stoppable { 623 624 private volatile boolean stop = false; 625 626 @Override 627 public void stop(String why) { 628 this.stop = true; 629 } 630 631 @Override 632 public boolean isStopped() { 633 return this.stop; 634 } 635 636 } 637 638}