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