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