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