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