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.balancer; 019 020import static org.junit.jupiter.api.Assertions.assertEquals; 021import static org.junit.jupiter.api.Assertions.assertTrue; 022import static org.mockito.Mockito.mock; 023import static org.mockito.Mockito.when; 024 025import java.io.IOException; 026import java.util.HashMap; 027import java.util.HashSet; 028import java.util.List; 029import java.util.Map; 030import java.util.Set; 031import java.util.TreeMap; 032import org.apache.hadoop.hbase.ClusterMetrics; 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.TableName; 038import org.apache.hadoop.hbase.client.RegionInfo; 039import org.apache.hadoop.hbase.master.RegionPlan; 040import org.apache.hadoop.hbase.rsgroup.RSGroupBasedLoadBalancer; 041import org.apache.hadoop.hbase.rsgroup.RSGroupInfo; 042import org.apache.hadoop.hbase.testclassification.LargeTests; 043import org.apache.hadoop.hbase.util.Bytes; 044import org.apache.hadoop.net.DNSToSwitchMapping; 045import org.junit.jupiter.api.BeforeAll; 046import org.junit.jupiter.api.Tag; 047import org.junit.jupiter.api.Test; 048 049@Tag(LargeTests.TAG) 050public class TestRSGroupBasedLoadBalancerWithStochasticLoadBalancerAsInternal 051 extends RSGroupableBalancerTestBase { 052 private static RSGroupBasedLoadBalancer loadBalancer; 053 054 @BeforeAll 055 public static void beforeAllTests() throws Exception { 056 groups = new String[] { RSGroupInfo.DEFAULT_GROUP }; 057 servers = generateServers(3); 058 groupMap = constructGroupInfo(servers, groups); 059 tableDescs = constructTableDesc(false); 060 conf.set("hbase.regions.slop", "0"); 061 conf.setFloat("hbase.master.balancer.stochastic.readRequestCost", 10000f); 062 conf.set("hbase.rsgroup.grouploadbalancer.class", 063 StochasticLoadBalancer.class.getCanonicalName()); 064 conf.setClass("hbase.util.ip.to.rack.determiner", MockMapping.class, DNSToSwitchMapping.class); 065 loadBalancer = new RSGroupBasedLoadBalancer(); 066 loadBalancer.setMasterServices(getMockedMaster()); 067 loadBalancer.initialize(); 068 } 069 070 private ServerMetrics mockServerMetricsWithReadRequests(ServerName server, 071 List<RegionInfo> regionsOnServer, long readRequestCount) { 072 ServerMetrics serverMetrics = mock(ServerMetrics.class); 073 Map<byte[], RegionMetrics> regionLoadMap = new TreeMap<>(Bytes.BYTES_COMPARATOR); 074 for (RegionInfo info : regionsOnServer) { 075 RegionMetrics rl = mock(RegionMetrics.class); 076 when(rl.getReadRequestCount()).thenReturn(readRequestCount); 077 when(rl.getCpRequestCount()).thenReturn(0L); 078 when(rl.getWriteRequestCount()).thenReturn(0L); 079 when(rl.getMemStoreSize()).thenReturn(Size.ZERO); 080 when(rl.getStoreFileSize()).thenReturn(Size.ZERO); 081 when(rl.getRegionSizeMB()).thenReturn(Size.ZERO); 082 when(rl.getCurrentRegionCachedRatio()).thenReturn(0.0f); 083 regionLoadMap.put(info.getRegionName(), rl); 084 } 085 when(serverMetrics.getRegionMetrics()).thenReturn(regionLoadMap); 086 return serverMetrics; 087 } 088 089 /** 090 * Test HBASE-20791 091 */ 092 @Test 093 public void testBalanceCluster() throws IOException { 094 // mock cluster State 095 Map<ServerName, List<RegionInfo>> clusterState = new HashMap<ServerName, List<RegionInfo>>(); 096 ServerName serverA = servers.get(0); 097 ServerName serverB = servers.get(1); 098 ServerName serverC = servers.get(2); 099 List<RegionInfo> regionsOnServerA = randomRegions(3); 100 List<RegionInfo> regionsOnServerB = randomRegions(3); 101 List<RegionInfo> regionsOnServerC = randomRegions(3); 102 clusterState.put(serverA, regionsOnServerA); 103 clusterState.put(serverB, regionsOnServerB); 104 clusterState.put(serverC, regionsOnServerC); 105 // mock ClusterMetrics 106 Map<ServerName, ServerMetrics> serverMetricsMap = new TreeMap<>(); 107 serverMetricsMap.put(serverA, mockServerMetricsWithReadRequests(serverA, regionsOnServerA, 0)); 108 serverMetricsMap.put(serverB, mockServerMetricsWithReadRequests(serverB, regionsOnServerB, 0)); 109 serverMetricsMap.put(serverC, mockServerMetricsWithReadRequests(serverC, regionsOnServerC, 0)); 110 ClusterMetrics clusterStatus = mock(ClusterMetrics.class); 111 when(clusterStatus.getLiveServerMetrics()).thenReturn(serverMetricsMap); 112 loadBalancer.updateClusterMetrics(clusterStatus); 113 114 // ReadRequestCostFunction are Rate based, So doing setClusterMetrics again 115 // this time, regions on serverA with more readRequestCount load 116 // serverA : 1000,1000,1000 117 // serverB : 0,0,0 118 // serverC : 0,0,0 119 // so should move two regions from serverA to serverB & serverC 120 serverMetricsMap = new TreeMap<>(); 121 serverMetricsMap.put(serverA, 122 mockServerMetricsWithReadRequests(serverA, regionsOnServerA, 1000)); 123 serverMetricsMap.put(serverB, mockServerMetricsWithReadRequests(serverB, regionsOnServerB, 0)); 124 serverMetricsMap.put(serverC, mockServerMetricsWithReadRequests(serverC, regionsOnServerC, 0)); 125 clusterStatus = mock(ClusterMetrics.class); 126 when(clusterStatus.getLiveServerMetrics()).thenReturn(serverMetricsMap); 127 loadBalancer.updateClusterMetrics(clusterStatus); 128 129 Map<TableName, Map<ServerName, List<RegionInfo>>> LoadOfAllTable = 130 (Map) mockClusterServersWithTables(clusterState); 131 List<RegionPlan> plans = loadBalancer.balanceCluster(LoadOfAllTable); 132 Set<RegionInfo> regionsMoveFromServerA = new HashSet<>(); 133 Set<ServerName> targetServers = new HashSet<>(); 134 for (RegionPlan plan : plans) { 135 if (plan.getSource().equals(serverA)) { 136 regionsMoveFromServerA.add(plan.getRegionInfo()); 137 targetServers.add(plan.getDestination()); 138 } 139 } 140 // should move 2 regions from serverA, one moves to serverB, the other moves to serverC 141 assertEquals(2, regionsMoveFromServerA.size()); 142 assertEquals(2, targetServers.size()); 143 assertTrue(regionsOnServerA.containsAll(regionsMoveFromServerA)); 144 } 145}