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.assertFalse; 022import static org.junit.Assert.assertTrue; 023 024import java.util.ArrayList; 025import java.util.HashMap; 026import java.util.Iterator; 027import java.util.List; 028import java.util.Map; 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.testclassification.LargeTests; 039import org.apache.hadoop.hbase.testclassification.MasterTests; 040import org.junit.ClassRule; 041import org.junit.Test; 042import org.junit.experimental.categories.Category; 043 044import org.apache.hbase.thirdparty.com.google.common.collect.ImmutableList; 045 046@Category({ MasterTests.class, LargeTests.class }) 047public class TestStochasticLoadBalancerRegionReplica extends StochasticBalancerTestBase { 048 049 @ClassRule 050 public static final HBaseClassTestRule CLASS_RULE = 051 HBaseClassTestRule.forClass(TestStochasticLoadBalancerRegionReplica.class); 052 053 @Test 054 public void testReplicaCost() { 055 Configuration conf = HBaseConfiguration.create(); 056 CostFunction costFunction = new RegionReplicaHostCostFunction(conf); 057 for (int[] mockCluster : clusterStateMocks) { 058 BalancerClusterState cluster = mockCluster(mockCluster); 059 costFunction.prepare(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 CostFunction costFunction = new RegionReplicaHostCostFunction(conf); 070 071 int[] servers = new int[] { 3, 3, 3, 3, 3 }; 072 TreeMap<ServerName, List<RegionInfo>> clusterState = mockClusterServers(servers); 073 074 BalancerClusterState cluster; 075 076 cluster = new BalancerClusterState(clusterState, null, null, null); 077 costFunction.prepare(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 BalancerClusterState(clusterState, null, null, null); 087 costFunction.prepare(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 BalancerClusterState(clusterState, null, null, null); 097 costFunction.prepare(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<Map.Entry<ServerName, List<RegionInfo>>> it; 106 Map.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 BalancerClusterState(clusterState, null, null, null); 120 costFunction.prepare(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 BalancerClusterState(clusterState, null, null, null); 134 costFunction.prepare(cluster); 135 double costWith2ReplicasOnTwoServers = costFunction.cost(); 136 137 assertTrue(costWith2ReplicasOnTwoServers < costWith3ReplicasSameServer); 138 } 139 140 @Test 141 public void testNeedsBalanceForColocatedReplicasOnHost() { 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 BalancerClusterState cluster = 154 new BalancerClusterState(map, null, null, new ForTestRackManagerOne()); 155 loadBalancer.initCosts(cluster); 156 assertTrue(loadBalancer.needsBalance(HConstants.ENSEMBLE_TABLE_NAME, cluster)); 157 } 158 159 @Test 160 public void testNeedsBalanceForColocatedReplicasOnRack() { 161 // Three hosts, two racks, and two replicas for a region. This should be balanced 162 List<RegionInfo> regions = randomRegions(1); 163 ServerName s1 = ServerName.valueOf("host1", 1000, 11111); 164 ServerName s2 = ServerName.valueOf("host11", 1000, 11111); 165 Map<ServerName, List<RegionInfo>> map = new HashMap<>(); 166 List<RegionInfo> regionsOnS2 = new ArrayList<>(1); 167 regionsOnS2.add(RegionReplicaUtil.getRegionInfoForReplica(regions.get(0), 1)); 168 map.put(s1, regions); 169 map.put(s2, regionsOnS2); 170 // add another server so that the cluster has some host on another rack 171 map.put(ServerName.valueOf("host2", 1000, 11111), randomRegions(1)); 172 BalancerClusterState cluster = 173 new BalancerClusterState(map, null, null, new ForTestRackManagerOne()); 174 loadBalancer.initCosts(cluster); 175 assertTrue(loadBalancer.needsBalance(HConstants.ENSEMBLE_TABLE_NAME, cluster)); 176 } 177 178 @Test 179 public void testNoNeededBalanceForColocatedReplicasTooFewRacks() { 180 // Three hosts, two racks, and three replicas for a region. This cannot be balanced 181 List<RegionInfo> regions = randomRegions(1); 182 ServerName s1 = ServerName.valueOf("host1", 1000, 11111); 183 ServerName s2 = ServerName.valueOf("host11", 1000, 11111); 184 ServerName s3 = ServerName.valueOf("host2", 1000, 11111); 185 Map<ServerName, List<RegionInfo>> map = new HashMap<>(); 186 List<RegionInfo> regionsOnS2 = new ArrayList<>(1); 187 regionsOnS2.add(RegionReplicaUtil.getRegionInfoForReplica(regions.get(0), 1)); 188 map.put(s1, regions); 189 map.put(s2, regionsOnS2); 190 // there are 3 replicas for region 0, but only add a second rack 191 map.put(s3, ImmutableList.of(RegionReplicaUtil.getRegionInfoForReplica(regions.get(0), 2))); 192 BalancerClusterState cluster = 193 new BalancerClusterState(map, null, null, new ForTestRackManagerOne()); 194 loadBalancer.initCosts(cluster); 195 // Should be false because there aren't enough racks 196 assertFalse(loadBalancer.needsBalance(HConstants.ENSEMBLE_TABLE_NAME, cluster)); 197 } 198 199 @Test 200 public void testRegionReplicasOnSmallCluster() { 201 int numNodes = 10; 202 int numRegions = 1000; 203 int replication = 3; // 3 replicas per region 204 int numRegionsPerServer = 80; // all regions are mostly balanced 205 int numTables = 10; 206 testWithClusterWithIteration(numNodes, numRegions, numRegionsPerServer, replication, numTables, 207 true, true); 208 } 209 210 private static class ForTestRackManagerOne extends RackManager { 211 @Override 212 public String getRack(ServerName server) { 213 return server.getHostname().endsWith("1") ? "rack1" : "rack2"; 214 } 215 } 216}