| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.hadoop.hbase.master.balancer; |
| |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertTrue; |
| import static org.mockito.Mockito.mock; |
| import static org.mockito.Mockito.when; |
| |
| import java.io.IOException; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.TreeMap; |
| import org.apache.hadoop.hbase.ClusterMetrics; |
| import org.apache.hadoop.hbase.HBaseClassTestRule; |
| import org.apache.hadoop.hbase.RegionMetrics; |
| import org.apache.hadoop.hbase.ServerMetrics; |
| import org.apache.hadoop.hbase.ServerName; |
| import org.apache.hadoop.hbase.Size; |
| import org.apache.hadoop.hbase.TableName; |
| import org.apache.hadoop.hbase.client.RegionInfo; |
| import org.apache.hadoop.hbase.master.RegionPlan; |
| import org.apache.hadoop.hbase.rsgroup.RSGroupBasedLoadBalancer; |
| import org.apache.hadoop.hbase.rsgroup.RSGroupInfo; |
| import org.apache.hadoop.hbase.testclassification.LargeTests; |
| import org.apache.hadoop.hbase.util.Bytes; |
| import org.apache.hadoop.net.DNSToSwitchMapping; |
| import org.junit.BeforeClass; |
| import org.junit.ClassRule; |
| import org.junit.Test; |
| import org.junit.experimental.categories.Category; |
| |
| /** |
| * Test RSGroupBasedLoadBalancer with StochasticLoadBalancer as internal balancer |
| */ |
| @Category(LargeTests.class) |
| public class TestRSGroupBasedLoadBalancerWithStochasticLoadBalancerAsInternal |
| extends RSGroupableBalancerTestBase { |
| @ClassRule |
| public static final HBaseClassTestRule CLASS_RULE = HBaseClassTestRule |
| .forClass(TestRSGroupBasedLoadBalancerWithStochasticLoadBalancerAsInternal.class); |
| private static RSGroupBasedLoadBalancer loadBalancer; |
| |
| @BeforeClass |
| public static void beforeAllTests() throws Exception { |
| groups = new String[] { RSGroupInfo.DEFAULT_GROUP }; |
| servers = generateServers(3); |
| groupMap = constructGroupInfo(servers, groups); |
| tableDescs = constructTableDesc(false); |
| conf.set("hbase.regions.slop", "0"); |
| conf.setFloat("hbase.master.balancer.stochastic.readRequestCost", 10000f); |
| conf.set("hbase.rsgroup.grouploadbalancer.class", |
| StochasticLoadBalancer.class.getCanonicalName()); |
| conf.setClass("hbase.util.ip.to.rack.determiner", MockMapping.class, DNSToSwitchMapping.class); |
| loadBalancer = new RSGroupBasedLoadBalancer(); |
| loadBalancer.setMasterServices(getMockedMaster()); |
| loadBalancer.initialize(); |
| } |
| |
| private ServerMetrics mockServerMetricsWithReadRequests(ServerName server, |
| List<RegionInfo> regionsOnServer, long readRequestCount) { |
| ServerMetrics serverMetrics = mock(ServerMetrics.class); |
| Map<byte[], RegionMetrics> regionLoadMap = new TreeMap<>(Bytes.BYTES_COMPARATOR); |
| for (RegionInfo info : regionsOnServer) { |
| RegionMetrics rl = mock(RegionMetrics.class); |
| when(rl.getReadRequestCount()).thenReturn(readRequestCount); |
| when(rl.getCpRequestCount()).thenReturn(0L); |
| when(rl.getWriteRequestCount()).thenReturn(0L); |
| when(rl.getMemStoreSize()).thenReturn(Size.ZERO); |
| when(rl.getStoreFileSize()).thenReturn(Size.ZERO); |
| when(rl.getRegionSizeMB()).thenReturn(Size.ZERO); |
| when(rl.getCurrentRegionCachedRatio()).thenReturn(0.0f); |
| regionLoadMap.put(info.getRegionName(), rl); |
| } |
| when(serverMetrics.getRegionMetrics()).thenReturn(regionLoadMap); |
| return serverMetrics; |
| } |
| |
| /** |
| * Test HBASE-20791 |
| */ |
| @Test |
| public void testBalanceCluster() throws IOException { |
| // mock cluster State |
| Map<ServerName, List<RegionInfo>> clusterState = new HashMap<ServerName, List<RegionInfo>>(); |
| ServerName serverA = servers.get(0); |
| ServerName serverB = servers.get(1); |
| ServerName serverC = servers.get(2); |
| List<RegionInfo> regionsOnServerA = randomRegions(3); |
| List<RegionInfo> regionsOnServerB = randomRegions(3); |
| List<RegionInfo> regionsOnServerC = randomRegions(3); |
| clusterState.put(serverA, regionsOnServerA); |
| clusterState.put(serverB, regionsOnServerB); |
| clusterState.put(serverC, regionsOnServerC); |
| // mock ClusterMetrics |
| Map<ServerName, ServerMetrics> serverMetricsMap = new TreeMap<>(); |
| serverMetricsMap.put(serverA, mockServerMetricsWithReadRequests(serverA, regionsOnServerA, 0)); |
| serverMetricsMap.put(serverB, mockServerMetricsWithReadRequests(serverB, regionsOnServerB, 0)); |
| serverMetricsMap.put(serverC, mockServerMetricsWithReadRequests(serverC, regionsOnServerC, 0)); |
| ClusterMetrics clusterStatus = mock(ClusterMetrics.class); |
| when(clusterStatus.getLiveServerMetrics()).thenReturn(serverMetricsMap); |
| loadBalancer.updateClusterMetrics(clusterStatus); |
| |
| // ReadRequestCostFunction are Rate based, So doing setClusterMetrics again |
| // this time, regions on serverA with more readRequestCount load |
| // serverA : 1000,1000,1000 |
| // serverB : 0,0,0 |
| // serverC : 0,0,0 |
| // so should move two regions from serverA to serverB & serverC |
| serverMetricsMap = new TreeMap<>(); |
| serverMetricsMap.put(serverA, |
| mockServerMetricsWithReadRequests(serverA, regionsOnServerA, 1000)); |
| serverMetricsMap.put(serverB, mockServerMetricsWithReadRequests(serverB, regionsOnServerB, 0)); |
| serverMetricsMap.put(serverC, mockServerMetricsWithReadRequests(serverC, regionsOnServerC, 0)); |
| clusterStatus = mock(ClusterMetrics.class); |
| when(clusterStatus.getLiveServerMetrics()).thenReturn(serverMetricsMap); |
| loadBalancer.updateClusterMetrics(clusterStatus); |
| |
| Map<TableName, Map<ServerName, List<RegionInfo>>> LoadOfAllTable = |
| (Map) mockClusterServersWithTables(clusterState); |
| List<RegionPlan> plans = loadBalancer.balanceCluster(LoadOfAllTable); |
| Set<RegionInfo> regionsMoveFromServerA = new HashSet<>(); |
| Set<ServerName> targetServers = new HashSet<>(); |
| for (RegionPlan plan : plans) { |
| if (plan.getSource().equals(serverA)) { |
| regionsMoveFromServerA.add(plan.getRegionInfo()); |
| targetServers.add(plan.getDestination()); |
| } |
| } |
| // should move 2 regions from serverA, one moves to serverB, the other moves to serverC |
| assertEquals(2, regionsMoveFromServerA.size()); |
| assertEquals(2, targetServers.size()); |
| assertTrue(regionsOnServerA.containsAll(regionsMoveFromServerA)); |
| } |
| } |