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