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; 022 023import java.util.ArrayList; 024import java.util.HashMap; 025import java.util.Iterator; 026import java.util.List; 027import java.util.Map; 028import java.util.Map.Entry; 029import java.util.TreeMap; 030import org.apache.hadoop.conf.Configuration; 031import org.apache.hadoop.hbase.HBaseClassTestRule; 032import org.apache.hadoop.hbase.HBaseConfiguration; 033import org.apache.hadoop.hbase.ServerName; 034import org.apache.hadoop.hbase.client.RegionInfo; 035import org.apache.hadoop.hbase.client.RegionReplicaUtil; 036import org.apache.hadoop.hbase.master.RackManager; 037import org.apache.hadoop.hbase.master.balancer.BaseLoadBalancer.Cluster; 038import org.apache.hadoop.hbase.testclassification.MasterTests; 039import org.apache.hadoop.hbase.testclassification.MediumTests; 040import org.junit.ClassRule; 041import org.junit.Test; 042import org.junit.experimental.categories.Category; 043 044@Category({ MasterTests.class, MediumTests.class }) 045public class TestStochasticLoadBalancerRegionReplica extends BalancerTestBase { 046 047 @ClassRule 048 public static final HBaseClassTestRule CLASS_RULE = 049 HBaseClassTestRule.forClass(TestStochasticLoadBalancerRegionReplica.class); 050 051 @Test 052 public void testReplicaCost() { 053 Configuration conf = HBaseConfiguration.create(); 054 StochasticLoadBalancer.CostFunction costFunction = 055 new StochasticLoadBalancer.RegionReplicaHostCostFunction(conf); 056 for (int[] mockCluster : clusterStateMocks) { 057 BaseLoadBalancer.Cluster cluster = mockCluster(mockCluster); 058 costFunction.init(cluster); 059 double cost = costFunction.cost(); 060 assertTrue(cost >= 0); 061 assertTrue(cost <= 1.01); 062 } 063 } 064 065 @Test 066 public void testReplicaCostForReplicas() { 067 Configuration conf = HBaseConfiguration.create(); 068 StochasticLoadBalancer.CostFunction costFunction = 069 new StochasticLoadBalancer.RegionReplicaHostCostFunction(conf); 070 071 int[] servers = new int[] { 3, 3, 3, 3, 3 }; 072 TreeMap<ServerName, List<RegionInfo>> clusterState = mockClusterServers(servers); 073 074 BaseLoadBalancer.Cluster cluster; 075 076 cluster = new BaseLoadBalancer.Cluster(clusterState, null, null, null); 077 costFunction.init(cluster); 078 double costWithoutReplicas = costFunction.cost(); 079 assertEquals(0, costWithoutReplicas, 0); 080 081 // replicate the region from first server to the last server 082 RegionInfo replica1 = 083 RegionReplicaUtil.getRegionInfoForReplica(clusterState.firstEntry().getValue().get(0), 1); 084 clusterState.lastEntry().getValue().add(replica1); 085 086 cluster = new BaseLoadBalancer.Cluster(clusterState, null, null, null); 087 costFunction.init(cluster); 088 double costWith1ReplicaDifferentServer = costFunction.cost(); 089 090 assertEquals(0, costWith1ReplicaDifferentServer, 0); 091 092 // add a third replica to the last server 093 RegionInfo replica2 = RegionReplicaUtil.getRegionInfoForReplica(replica1, 2); 094 clusterState.lastEntry().getValue().add(replica2); 095 096 cluster = new BaseLoadBalancer.Cluster(clusterState, null, null, null); 097 costFunction.init(cluster); 098 double costWith1ReplicaSameServer = costFunction.cost(); 099 100 assertTrue(costWith1ReplicaDifferentServer < costWith1ReplicaSameServer); 101 102 // test with replication = 4 for following: 103 104 RegionInfo replica3; 105 Iterator<Entry<ServerName, List<RegionInfo>>> it; 106 Entry<ServerName, List<RegionInfo>> entry; 107 108 clusterState = mockClusterServers(servers); 109 it = clusterState.entrySet().iterator(); 110 entry = it.next(); // first server 111 RegionInfo hri = entry.getValue().get(0); 112 replica1 = RegionReplicaUtil.getRegionInfoForReplica(hri, 1); 113 replica2 = RegionReplicaUtil.getRegionInfoForReplica(hri, 2); 114 replica3 = RegionReplicaUtil.getRegionInfoForReplica(hri, 3); 115 entry.getValue().add(replica1); 116 entry.getValue().add(replica2); 117 it.next().getValue().add(replica3); // 2nd server 118 119 cluster = new BaseLoadBalancer.Cluster(clusterState, null, null, null); 120 costFunction.init(cluster); 121 double costWith3ReplicasSameServer = costFunction.cost(); 122 123 clusterState = mockClusterServers(servers); 124 hri = clusterState.firstEntry().getValue().get(0); 125 replica1 = RegionReplicaUtil.getRegionInfoForReplica(hri, 1); 126 replica2 = RegionReplicaUtil.getRegionInfoForReplica(hri, 2); 127 replica3 = RegionReplicaUtil.getRegionInfoForReplica(hri, 3); 128 129 clusterState.firstEntry().getValue().add(replica1); 130 clusterState.lastEntry().getValue().add(replica2); 131 clusterState.lastEntry().getValue().add(replica3); 132 133 cluster = new BaseLoadBalancer.Cluster(clusterState, null, null, null); 134 costFunction.init(cluster); 135 double costWith2ReplicasOnTwoServers = costFunction.cost(); 136 137 assertTrue(costWith2ReplicasOnTwoServers < costWith3ReplicasSameServer); 138 } 139 140 @Test 141 public void testNeedsBalanceForColocatedReplicas() { 142 // check for the case where there are two hosts and with one rack, and where 143 // both the replicas are hosted on the same server 144 List<RegionInfo> regions = randomRegions(1); 145 ServerName s1 = ServerName.valueOf("host1", 1000, 11111); 146 ServerName s2 = ServerName.valueOf("host11", 1000, 11111); 147 Map<ServerName, List<RegionInfo>> map = new HashMap<>(); 148 map.put(s1, regions); 149 regions.add(RegionReplicaUtil.getRegionInfoForReplica(regions.get(0), 1)); 150 // until the step above s1 holds two replicas of a region 151 regions = randomRegions(1); 152 map.put(s2, regions); 153 assertTrue(loadBalancer.needsBalance(new Cluster(map, null, null, null))); 154 // check for the case where there are two hosts on the same rack and there are two racks 155 // and both the replicas are on the same rack 156 map.clear(); 157 regions = randomRegions(1); 158 List<RegionInfo> regionsOnS2 = new ArrayList<>(1); 159 regionsOnS2.add(RegionReplicaUtil.getRegionInfoForReplica(regions.get(0), 1)); 160 map.put(s1, regions); 161 map.put(s2, regionsOnS2); 162 // add another server so that the cluster has some host on another rack 163 map.put(ServerName.valueOf("host2", 1000, 11111), randomRegions(1)); 164 assertTrue( 165 loadBalancer.needsBalance(new Cluster(map, null, null, new ForTestRackManagerOne()))); 166 } 167 168 @Test 169 public void testRegionReplicasOnSmallCluster() { 170 int numNodes = 10; 171 int numRegions = 1000; 172 int replication = 3; // 3 replicas per region 173 int numRegionsPerServer = 80; // all regions are mostly balanced 174 int numTables = 10; 175 testWithCluster(numNodes, numRegions, numRegionsPerServer, replication, numTables, true, true); 176 } 177 178 private static class ForTestRackManagerOne extends RackManager { 179 @Override 180 public String getRack(ServerName server) { 181 return server.getHostname().endsWith("1") ? "rack1" : "rack2"; 182 } 183 } 184}