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 return serverMetrics; 404 } 405 406 private static RegionMetrics getRegionMetrics(byte[] regionName, int compactedStoreRefCount) { 407 RegionMetrics regionMetrics = new RegionMetrics() { 408 409 @Override 410 public byte[] getRegionName() { 411 return regionName; 412 } 413 414 @Override 415 public int getStoreCount() { 416 return 0; 417 } 418 419 @Override 420 public int getStoreFileCount() { 421 return 0; 422 } 423 424 @Override 425 public Size getStoreFileSize() { 426 return null; 427 } 428 429 @Override 430 public Size getMemStoreSize() { 431 return null; 432 } 433 434 @Override 435 public long getReadRequestCount() { 436 return 0; 437 } 438 439 @Override 440 public long getWriteRequestCount() { 441 return 0; 442 } 443 444 @Override 445 public long getCpRequestCount() { 446 return 0; 447 } 448 449 @Override 450 public long getFilteredReadRequestCount() { 451 return 0; 452 } 453 454 @Override 455 public Size getStoreFileIndexSize() { 456 return null; 457 } 458 459 @Override 460 public Size getStoreFileRootLevelIndexSize() { 461 return null; 462 } 463 464 @Override 465 public Size getStoreFileUncompressedDataIndexSize() { 466 return null; 467 } 468 469 @Override 470 public Size getBloomFilterSize() { 471 return null; 472 } 473 474 @Override 475 public long getCompactingCellCount() { 476 return 0; 477 } 478 479 @Override 480 public long getCompactedCellCount() { 481 return 0; 482 } 483 484 @Override 485 public long getCompletedSequenceId() { 486 return 0; 487 } 488 489 @Override 490 public Map<byte[], Long> getStoreSequenceId() { 491 return null; 492 } 493 494 @Override 495 public Size getUncompressedStoreFileSize() { 496 return null; 497 } 498 499 @Override 500 public float getDataLocality() { 501 return 0; 502 } 503 504 @Override 505 public long getLastMajorCompactionTimestamp() { 506 return 0; 507 } 508 509 @Override 510 public int getStoreRefCount() { 511 return compactedStoreRefCount; 512 } 513 514 @Override 515 public int getMaxCompactedStoreFileRefCount() { 516 return compactedStoreRefCount; 517 } 518 519 @Override 520 public float getDataLocalityForSsd() { 521 return 0; 522 } 523 524 @Override 525 public long getBlocksLocalWeight() { 526 return 0; 527 } 528 529 @Override 530 public long getBlocksLocalWithSsdWeight() { 531 return 0; 532 } 533 534 @Override 535 public long getBlocksTotalWeight() { 536 return 0; 537 } 538 539 @Override 540 public CompactionState getCompactionState() { 541 return null; 542 } 543 544 @Override 545 public Size getRegionSizeMB() { 546 return null; 547 } 548 549 @Override 550 public float getCurrentRegionCachedRatio() { 551 return 0.0f; 552 } 553 }; 554 return regionMetrics; 555 } 556 557 private static RegionInfo getRegionInfo(byte[] regionNameBytes) { 558 RegionInfo regionInfo = new RegionInfo() { 559 560 @Override 561 public String getShortNameToLog() { 562 return null; 563 } 564 565 @Override 566 public long getRegionId() { 567 return 0; 568 } 569 570 @Override 571 public byte[] getRegionName() { 572 return new byte[0]; 573 } 574 575 @Override 576 public String getRegionNameAsString() { 577 try { 578 return new String(regionNameBytes, UTF_8_CHARSET); 579 } catch (UnsupportedEncodingException e) { 580 return ""; 581 } 582 } 583 584 @Override 585 public String getEncodedName() { 586 return null; 587 } 588 589 @Override 590 public byte[] getEncodedNameAsBytes() { 591 return new byte[0]; 592 } 593 594 @Override 595 public byte[] getStartKey() { 596 return new byte[0]; 597 } 598 599 @Override 600 public byte[] getEndKey() { 601 return new byte[0]; 602 } 603 604 @Override 605 public TableName getTable() { 606 String regionName; 607 try { 608 regionName = new String(regionNameBytes, UTF_8_CHARSET); 609 } catch (UnsupportedEncodingException e) { 610 regionName = ""; 611 } 612 int regionNo = Integer.parseInt(regionName.split("_")[1]); 613 TableName tableName = TableName.valueOf("table_" + regionNo % 3); 614 return tableName; 615 } 616 617 @Override 618 public int getReplicaId() { 619 return 0; 620 } 621 622 @Override 623 public boolean isSplit() { 624 return false; 625 } 626 627 @Override 628 public boolean isOffline() { 629 return false; 630 } 631 632 @Override 633 public boolean isSplitParent() { 634 return false; 635 } 636 637 @Override 638 public boolean isMetaRegion() { 639 return false; 640 } 641 642 @Override 643 public boolean containsRange(byte[] rangeStartKey, byte[] rangeEndKey) { 644 return false; 645 } 646 647 @Override 648 public boolean containsRow(byte[] row) { 649 return false; 650 } 651 652 }; 653 return regionInfo; 654 } 655 656 /** 657 * Simple helper class that just keeps track of whether or not its stopped. 658 */ 659 private static class StoppableImplementation implements Stoppable { 660 661 private volatile boolean stop = false; 662 663 @Override 664 public void stop(String why) { 665 this.stop = true; 666 } 667 668 @Override 669 public boolean isStopped() { 670 return this.stop; 671 } 672 673 } 674 675}