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.HBaseClassTestRule; 030import org.apache.hadoop.hbase.HBaseTestingUtility; 031import org.apache.hadoop.hbase.RegionMetrics; 032import org.apache.hadoop.hbase.ServerMetrics; 033import org.apache.hadoop.hbase.ServerName; 034import org.apache.hadoop.hbase.ServerTask; 035import org.apache.hadoop.hbase.Size; 036import org.apache.hadoop.hbase.Stoppable; 037import org.apache.hadoop.hbase.TableName; 038import org.apache.hadoop.hbase.UserMetrics; 039import org.apache.hadoop.hbase.client.CompactionState; 040import org.apache.hadoop.hbase.client.RegionInfo; 041import org.apache.hadoop.hbase.client.RegionStatesCount; 042import org.apache.hadoop.hbase.master.assignment.AssignmentManager; 043import org.apache.hadoop.hbase.replication.ReplicationLoadSink; 044import org.apache.hadoop.hbase.replication.ReplicationLoadSource; 045import org.apache.hadoop.hbase.testclassification.MasterTests; 046import org.apache.hadoop.hbase.testclassification.SmallTests; 047import org.apache.hadoop.hbase.util.Bytes; 048import org.junit.After; 049import org.junit.Before; 050import org.junit.ClassRule; 051import org.junit.Test; 052import org.junit.experimental.categories.Category; 053import org.mockito.Mockito; 054import org.slf4j.Logger; 055import org.slf4j.LoggerFactory; 056 057/** 058 * Test for RegionsRecoveryChore 059 */ 060@Category({ MasterTests.class, SmallTests.class }) 061public class TestRegionsRecoveryChore { 062 063 @ClassRule 064 public static final HBaseClassTestRule CLASS_RULE = 065 HBaseClassTestRule.forClass(TestRegionsRecoveryChore.class); 066 067 private static final Logger LOG = LoggerFactory.getLogger(TestRegionsRecoveryChore.class); 068 069 private static final HBaseTestingUtility HBASE_TESTING_UTILITY = new HBaseTestingUtility(); 070 071 private static final String UTF_8_CHARSET = StandardCharsets.UTF_8.name(); 072 073 private HMaster hMaster; 074 075 private AssignmentManager assignmentManager; 076 077 private RegionsRecoveryChore regionsRecoveryChore; 078 079 private static int regionNo; 080 public static final byte[][] REGION_NAME_LIST = 081 new byte[][] { new byte[] { 114, 101, 103, 105, 111, 110, 50, 49, 95, 51 }, 082 new byte[] { 114, 101, 103, 105, 111, 110, 50, 53, 95, 51 }, 083 new byte[] { 114, 101, 103, 105, 111, 110, 50, 54, 95, 52 }, 084 new byte[] { 114, 101, 103, 105, 111, 110, 51, 50, 95, 53 }, 085 new byte[] { 114, 101, 103, 105, 111, 110, 51, 49, 95, 52 }, 086 new byte[] { 114, 101, 103, 105, 111, 110, 51, 48, 95, 51 }, 087 new byte[] { 114, 101, 103, 105, 111, 110, 50, 48, 95, 50 }, 088 new byte[] { 114, 101, 103, 105, 111, 110, 50, 52, 95, 50 }, 089 new byte[] { 114, 101, 103, 105, 111, 110, 50, 57, 95, 50 }, 090 new byte[] { 114, 101, 103, 105, 111, 110, 51, 53, 95, 50 }, 091 new byte[] { 114, 101, 103, 105, 111, 110, 49, 48, 56, 95, 49, 49 } }; 092 093 private Configuration getCustomConf() { 094 Configuration conf = HBASE_TESTING_UTILITY.getConfiguration(); 095 conf.setInt("hbase.master.regions.recovery.check.interval", 100); 096 return conf; 097 } 098 099 @Before 100 public void setUp() throws Exception { 101 this.hMaster = Mockito.mock(HMaster.class); 102 this.assignmentManager = Mockito.mock(AssignmentManager.class); 103 } 104 105 @After 106 public void tearDown() throws Exception { 107 Mockito.verifyNoMoreInteractions(this.hMaster); 108 Mockito.verifyNoMoreInteractions(this.assignmentManager); 109 } 110 111 @Test 112 public void testRegionReopensWithStoreRefConfig() throws Exception { 113 regionNo = 0; 114 ClusterMetrics clusterMetrics = TestRegionsRecoveryChore.getClusterMetrics(4); 115 final Map<ServerName, ServerMetrics> serverMetricsMap = clusterMetrics.getLiveServerMetrics(); 116 LOG.debug("All Region Names with refCount...."); 117 for (ServerMetrics serverMetrics : serverMetricsMap.values()) { 118 Map<byte[], RegionMetrics> regionMetricsMap = serverMetrics.getRegionMetrics(); 119 for (RegionMetrics regionMetrics : regionMetricsMap.values()) { 120 LOG.debug("name: " + new String(regionMetrics.getRegionName()) + " refCount: " 121 + regionMetrics.getStoreRefCount()); 122 } 123 } 124 Mockito.when(hMaster.getClusterMetrics()).thenReturn(clusterMetrics); 125 Mockito.when(hMaster.getAssignmentManager()).thenReturn(assignmentManager); 126 for (byte[] regionName : REGION_NAME_LIST) { 127 Mockito.when(assignmentManager.getRegionInfo(regionName)) 128 .thenReturn(TestRegionsRecoveryChore.getRegionInfo(regionName)); 129 } 130 Stoppable stoppable = new StoppableImplementation(); 131 Configuration configuration = getCustomConf(); 132 configuration.setInt("hbase.regions.recovery.store.file.ref.count", 300); 133 regionsRecoveryChore = new RegionsRecoveryChore(stoppable, configuration, hMaster); 134 regionsRecoveryChore.chore(); 135 136 // Verify that we need to reopen regions of 2 tables 137 Mockito.verify(hMaster, Mockito.times(2)).reopenRegions(Mockito.any(), Mockito.anyList(), 138 Mockito.anyLong(), Mockito.anyLong()); 139 Mockito.verify(hMaster, Mockito.times(1)).getClusterMetrics(); 140 141 // Verify that we need to reopen total 3 regions that have refCount > 300 142 Mockito.verify(hMaster, Mockito.times(3)).getAssignmentManager(); 143 Mockito.verify(assignmentManager, Mockito.times(3)).getRegionInfo(Mockito.any(byte[].class)); 144 } 145 146 @Test 147 public void testRegionReopensWithLessThreshold() throws Exception { 148 regionNo = 0; 149 ClusterMetrics clusterMetrics = TestRegionsRecoveryChore.getClusterMetrics(4); 150 final Map<ServerName, ServerMetrics> serverMetricsMap = clusterMetrics.getLiveServerMetrics(); 151 LOG.debug("All Region Names with refCount...."); 152 for (ServerMetrics serverMetrics : serverMetricsMap.values()) { 153 Map<byte[], RegionMetrics> regionMetricsMap = serverMetrics.getRegionMetrics(); 154 for (RegionMetrics regionMetrics : regionMetricsMap.values()) { 155 LOG.debug("name: " + new String(regionMetrics.getRegionName()) + " refCount: " 156 + regionMetrics.getStoreRefCount()); 157 } 158 } 159 Mockito.when(hMaster.getClusterMetrics()).thenReturn(clusterMetrics); 160 Mockito.when(hMaster.getAssignmentManager()).thenReturn(assignmentManager); 161 for (byte[] regionName : REGION_NAME_LIST) { 162 Mockito.when(assignmentManager.getRegionInfo(regionName)) 163 .thenReturn(TestRegionsRecoveryChore.getRegionInfo(regionName)); 164 } 165 Stoppable stoppable = new StoppableImplementation(); 166 Configuration configuration = getCustomConf(); 167 configuration.setInt("hbase.regions.recovery.store.file.ref.count", 400); 168 regionsRecoveryChore = new RegionsRecoveryChore(stoppable, configuration, hMaster); 169 regionsRecoveryChore.chore(); 170 171 // Verify that we need to reopen regions of only 1 table 172 Mockito.verify(hMaster, Mockito.times(1)).reopenRegions(Mockito.any(), Mockito.anyList(), 173 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 // Verify that by default the feature is turned off so no regions 207 // should be reopened 208 Mockito.verify(hMaster, Mockito.times(0)).reopenRegions(Mockito.any(), Mockito.anyList(), 209 Mockito.anyLong(), Mockito.anyLong()); 210 211 // default maxCompactedStoreFileRefCount is -1 (no regions to be reopened using AM) 212 Mockito.verify(hMaster, Mockito.times(0)).getAssignmentManager(); 213 Mockito.verify(assignmentManager, Mockito.times(0)).getRegionInfo(Mockito.any(byte[].class)); 214 } 215 216 private static ClusterMetrics getClusterMetrics(int noOfLiveServer) { 217 ClusterMetrics clusterMetrics = new ClusterMetrics() { 218 219 @Nullable 220 @Override 221 public String getHBaseVersion() { 222 return null; 223 } 224 225 @Override 226 public List<ServerName> getDeadServerNames() { 227 return null; 228 } 229 230 @Override 231 public List<ServerName> getUnknownServerNames() { 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 Size getUsedHeapSize() { 322 return null; 323 } 324 325 @Override 326 public Size getMaxHeapSize() { 327 return null; 328 } 329 330 @Override 331 public int getInfoServerPort() { 332 return 0; 333 } 334 335 @Override 336 public List<ReplicationLoadSource> getReplicationLoadSourceList() { 337 return null; 338 } 339 340 @Override 341 public Map<String, List<ReplicationLoadSource>> getReplicationLoadSourceMap() { 342 return null; 343 } 344 345 @Nullable 346 @Override 347 public ReplicationLoadSink getReplicationLoadSink() { 348 return null; 349 } 350 351 @Override 352 public Map<byte[], RegionMetrics> getRegionMetrics() { 353 Map<byte[], RegionMetrics> regionMetricsMap = new HashMap<>(); 354 for (int i = 0; i < noOfRegions; i++) { 355 byte[] regionName = Bytes.toBytes("region" + regionNo + "_" + i); 356 regionMetricsMap.put(regionName, 357 TestRegionsRecoveryChore.getRegionMetrics(regionName, 100 * i)); 358 ++regionNo; 359 } 360 return regionMetricsMap; 361 } 362 363 @Override 364 public Map<byte[], UserMetrics> getUserMetrics() { 365 return new HashMap<>(); 366 } 367 368 @Override 369 public Set<String> getCoprocessorNames() { 370 return null; 371 } 372 373 @Override 374 public long getReportTimestamp() { 375 return 0; 376 } 377 378 @Override 379 public long getLastReportTimestamp() { 380 return 0; 381 } 382 383 @Override 384 public List<ServerTask> getTasks() { 385 return null; 386 } 387 388 @Override 389 public Map<String, Integer> getRegionCachedInfo() { 390 return new HashMap<>(); 391 } 392 }; 393 return serverMetrics; 394 } 395 396 private static RegionMetrics getRegionMetrics(byte[] regionName, int compactedStoreRefCount) { 397 RegionMetrics regionMetrics = new RegionMetrics() { 398 399 @Override 400 public byte[] getRegionName() { 401 return regionName; 402 } 403 404 @Override 405 public int getStoreCount() { 406 return 0; 407 } 408 409 @Override 410 public int getStoreFileCount() { 411 return 0; 412 } 413 414 @Override 415 public Size getStoreFileSize() { 416 return null; 417 } 418 419 @Override 420 public Size getMemStoreSize() { 421 return null; 422 } 423 424 @Override 425 public long getReadRequestCount() { 426 return 0; 427 } 428 429 @Override 430 public long getWriteRequestCount() { 431 return 0; 432 } 433 434 @Override 435 public long getFilteredReadRequestCount() { 436 return 0; 437 } 438 439 @Override 440 public Size getStoreFileIndexSize() { 441 return null; 442 } 443 444 @Override 445 public Size getStoreFileRootLevelIndexSize() { 446 return null; 447 } 448 449 @Override 450 public Size getStoreFileUncompressedDataIndexSize() { 451 return null; 452 } 453 454 @Override 455 public Size getBloomFilterSize() { 456 return null; 457 } 458 459 @Override 460 public long getCompactingCellCount() { 461 return 0; 462 } 463 464 @Override 465 public long getCompactedCellCount() { 466 return 0; 467 } 468 469 @Override 470 public long getCompletedSequenceId() { 471 return 0; 472 } 473 474 @Override 475 public Map<byte[], Long> getStoreSequenceId() { 476 return null; 477 } 478 479 @Override 480 public Size getUncompressedStoreFileSize() { 481 return null; 482 } 483 484 @Override 485 public float getDataLocality() { 486 return 0; 487 } 488 489 @Override 490 public long getLastMajorCompactionTimestamp() { 491 return 0; 492 } 493 494 @Override 495 public int getStoreRefCount() { 496 return compactedStoreRefCount; 497 } 498 499 @Override 500 public int getMaxCompactedStoreFileRefCount() { 501 return compactedStoreRefCount; 502 } 503 504 @Override 505 public float getDataLocalityForSsd() { 506 return 0; 507 } 508 509 @Override 510 public long getBlocksLocalWeight() { 511 return 0; 512 } 513 514 @Override 515 public long getBlocksLocalWithSsdWeight() { 516 return 0; 517 } 518 519 @Override 520 public long getBlocksTotalWeight() { 521 return 0; 522 } 523 524 @Override 525 public CompactionState getCompactionState() { 526 return null; 527 } 528 529 @Override 530 public Size getRegionSizeMB() { 531 return null; 532 } 533 534 @Override 535 public float getCurrentRegionCachedRatio() { 536 return 0.0f; 537 } 538 }; 539 return regionMetrics; 540 } 541 542 private static RegionInfo getRegionInfo(byte[] regionNameBytes) { 543 RegionInfo regionInfo = new RegionInfo() { 544 545 @Override 546 public String getShortNameToLog() { 547 return null; 548 } 549 550 @Override 551 public long getRegionId() { 552 return 0; 553 } 554 555 @Override 556 public byte[] getRegionName() { 557 return new byte[0]; 558 } 559 560 @Override 561 public String getRegionNameAsString() { 562 try { 563 return new String(regionNameBytes, UTF_8_CHARSET); 564 } catch (UnsupportedEncodingException e) { 565 return ""; 566 } 567 } 568 569 @Override 570 public String getEncodedName() { 571 return null; 572 } 573 574 @Override 575 public byte[] getEncodedNameAsBytes() { 576 return new byte[0]; 577 } 578 579 @Override 580 public byte[] getStartKey() { 581 return new byte[0]; 582 } 583 584 @Override 585 public byte[] getEndKey() { 586 return new byte[0]; 587 } 588 589 @Override 590 public TableName getTable() { 591 String regionName; 592 try { 593 regionName = new String(regionNameBytes, UTF_8_CHARSET); 594 } catch (UnsupportedEncodingException e) { 595 regionName = ""; 596 } 597 int regionNo = Integer.parseInt(regionName.split("_")[1]); 598 TableName tableName = TableName.valueOf("table_" + regionNo % 3); 599 return tableName; 600 } 601 602 @Override 603 public int getReplicaId() { 604 return 0; 605 } 606 607 @Override 608 public boolean isSplit() { 609 return false; 610 } 611 612 @Override 613 public boolean isOffline() { 614 return false; 615 } 616 617 @Override 618 public boolean isSplitParent() { 619 return false; 620 } 621 622 @Override 623 public boolean isMetaRegion() { 624 return false; 625 } 626 627 @Override 628 public boolean containsRange(byte[] rangeStartKey, byte[] rangeEndKey) { 629 return false; 630 } 631 632 @Override 633 public boolean containsRow(byte[] row) { 634 return false; 635 } 636 637 }; 638 return regionInfo; 639 } 640 641 /** 642 * Simple helper class that just keeps track of whether or not its stopped. 643 */ 644 private static class StoppableImplementation implements Stoppable { 645 646 private volatile boolean stop = false; 647 648 @Override 649 public void stop(String why) { 650 this.stop = true; 651 } 652 653 @Override 654 public boolean isStopped() { 655 return this.stop; 656 } 657 658 } 659 660}