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.jupiter.api.Assertions.assertEquals; 021import static org.junit.jupiter.api.Assertions.assertFalse; 022import static org.junit.jupiter.api.Assertions.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.HBaseConfiguration; 032import org.apache.hadoop.hbase.HConstants; 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.testclassification.LargeTests; 038import org.apache.hadoop.hbase.testclassification.MasterTests; 039import org.junit.jupiter.api.Tag; 040import org.junit.jupiter.api.Test; 041 042import org.apache.hbase.thirdparty.com.google.common.collect.ImmutableList; 043 044@Tag(MasterTests.TAG) 045@Tag(LargeTests.TAG) 046public class TestStochasticLoadBalancerRegionReplica extends StochasticBalancerTestBase { 047 048 @Test 049 public void testReplicaCost() { 050 Configuration conf = HBaseConfiguration.create(); 051 CostFunction costFunction = new RegionReplicaHostCostFunction(conf); 052 for (int[] mockCluster : clusterStateMocks) { 053 BalancerClusterState cluster = mockCluster(mockCluster); 054 costFunction.prepare(cluster); 055 double cost = costFunction.cost(); 056 assertTrue(cost >= 0); 057 assertTrue(cost <= 1.01); 058 } 059 } 060 061 @Test 062 public void testReplicaCostForReplicas() { 063 Configuration conf = HBaseConfiguration.create(); 064 CostFunction costFunction = new RegionReplicaHostCostFunction(conf); 065 066 int[] servers = new int[] { 3, 3, 3, 3, 3 }; 067 TreeMap<ServerName, List<RegionInfo>> clusterState = mockClusterServers(servers); 068 069 BalancerClusterState cluster; 070 071 cluster = new BalancerClusterState(clusterState, null, null, null); 072 costFunction.prepare(cluster); 073 double costWithoutReplicas = costFunction.cost(); 074 assertEquals(0, costWithoutReplicas, 0); 075 076 // replicate the region from first server to the last server 077 RegionInfo replica1 = 078 RegionReplicaUtil.getRegionInfoForReplica(clusterState.firstEntry().getValue().get(0), 1); 079 clusterState.lastEntry().getValue().add(replica1); 080 081 cluster = new BalancerClusterState(clusterState, null, null, null); 082 costFunction.prepare(cluster); 083 double costWith1ReplicaDifferentServer = costFunction.cost(); 084 085 assertEquals(0, costWith1ReplicaDifferentServer, 0); 086 087 // add a third replica to the last server 088 RegionInfo replica2 = RegionReplicaUtil.getRegionInfoForReplica(replica1, 2); 089 clusterState.lastEntry().getValue().add(replica2); 090 091 cluster = new BalancerClusterState(clusterState, null, null, null); 092 costFunction.prepare(cluster); 093 double costWith1ReplicaSameServer = costFunction.cost(); 094 095 assertTrue(costWith1ReplicaDifferentServer < costWith1ReplicaSameServer); 096 097 // test with replication = 4 for following: 098 099 RegionInfo replica3; 100 Iterator<Map.Entry<ServerName, List<RegionInfo>>> it; 101 Map.Entry<ServerName, List<RegionInfo>> entry; 102 103 clusterState = mockClusterServers(servers); 104 it = clusterState.entrySet().iterator(); 105 entry = it.next(); // first server 106 RegionInfo hri = entry.getValue().get(0); 107 replica1 = RegionReplicaUtil.getRegionInfoForReplica(hri, 1); 108 replica2 = RegionReplicaUtil.getRegionInfoForReplica(hri, 2); 109 replica3 = RegionReplicaUtil.getRegionInfoForReplica(hri, 3); 110 entry.getValue().add(replica1); 111 entry.getValue().add(replica2); 112 it.next().getValue().add(replica3); // 2nd server 113 114 cluster = new BalancerClusterState(clusterState, null, null, null); 115 costFunction.prepare(cluster); 116 double costWith3ReplicasSameServer = costFunction.cost(); 117 118 clusterState = mockClusterServers(servers); 119 hri = clusterState.firstEntry().getValue().get(0); 120 replica1 = RegionReplicaUtil.getRegionInfoForReplica(hri, 1); 121 replica2 = RegionReplicaUtil.getRegionInfoForReplica(hri, 2); 122 replica3 = RegionReplicaUtil.getRegionInfoForReplica(hri, 3); 123 124 clusterState.firstEntry().getValue().add(replica1); 125 clusterState.lastEntry().getValue().add(replica2); 126 clusterState.lastEntry().getValue().add(replica3); 127 128 cluster = new BalancerClusterState(clusterState, null, null, null); 129 costFunction.prepare(cluster); 130 double costWith2ReplicasOnTwoServers = costFunction.cost(); 131 132 assertTrue(costWith2ReplicasOnTwoServers < costWith3ReplicasSameServer); 133 } 134 135 @Test 136 public void testNeedsBalanceForColocatedReplicasOnHost() { 137 // check for the case where there are two hosts and with one rack, and where 138 // both the replicas are hosted on the same server 139 List<RegionInfo> regions = randomRegions(1); 140 ServerName s1 = ServerName.valueOf("host1", 1000, 11111); 141 ServerName s2 = ServerName.valueOf("host11", 1000, 11111); 142 Map<ServerName, List<RegionInfo>> map = new HashMap<>(); 143 map.put(s1, regions); 144 regions.add(RegionReplicaUtil.getRegionInfoForReplica(regions.get(0), 1)); 145 // until the step above s1 holds two replicas of a region 146 regions = randomRegions(1); 147 map.put(s2, regions); 148 BalancerClusterState cluster = 149 new BalancerClusterState(map, null, null, new ForTestRackManagerOne()); 150 loadBalancer.initCosts(cluster); 151 assertTrue(loadBalancer.needsBalance(HConstants.ENSEMBLE_TABLE_NAME, cluster)); 152 } 153 154 @Test 155 public void testNeedsBalanceForColocatedReplicasOnRack() { 156 // Three hosts, two racks, and two replicas for a region. This should be balanced 157 List<RegionInfo> regions = randomRegions(1); 158 ServerName s1 = ServerName.valueOf("host1", 1000, 11111); 159 ServerName s2 = ServerName.valueOf("host11", 1000, 11111); 160 Map<ServerName, List<RegionInfo>> map = new HashMap<>(); 161 List<RegionInfo> regionsOnS2 = new ArrayList<>(1); 162 regionsOnS2.add(RegionReplicaUtil.getRegionInfoForReplica(regions.get(0), 1)); 163 map.put(s1, regions); 164 map.put(s2, regionsOnS2); 165 // add another server so that the cluster has some host on another rack 166 map.put(ServerName.valueOf("host2", 1000, 11111), randomRegions(1)); 167 BalancerClusterState cluster = 168 new BalancerClusterState(map, null, null, new ForTestRackManagerOne()); 169 loadBalancer.initCosts(cluster); 170 assertTrue(loadBalancer.needsBalance(HConstants.ENSEMBLE_TABLE_NAME, cluster)); 171 } 172 173 @Test 174 public void testNoNeededBalanceForColocatedReplicasTooFewRacks() { 175 // Three hosts, two racks, and three replicas for a region. This cannot be balanced 176 List<RegionInfo> regions = randomRegions(1); 177 ServerName s1 = ServerName.valueOf("host1", 1000, 11111); 178 ServerName s2 = ServerName.valueOf("host11", 1000, 11111); 179 ServerName s3 = ServerName.valueOf("host2", 1000, 11111); 180 Map<ServerName, List<RegionInfo>> map = new HashMap<>(); 181 List<RegionInfo> regionsOnS2 = new ArrayList<>(1); 182 regionsOnS2.add(RegionReplicaUtil.getRegionInfoForReplica(regions.get(0), 1)); 183 map.put(s1, regions); 184 map.put(s2, regionsOnS2); 185 // there are 3 replicas for region 0, but only add a second rack 186 map.put(s3, ImmutableList.of(RegionReplicaUtil.getRegionInfoForReplica(regions.get(0), 2))); 187 BalancerClusterState cluster = 188 new BalancerClusterState(map, null, null, new ForTestRackManagerOne()); 189 loadBalancer.initCosts(cluster); 190 // Should be false because there aren't enough racks 191 assertFalse(loadBalancer.needsBalance(HConstants.ENSEMBLE_TABLE_NAME, cluster)); 192 } 193 194 @Test 195 public void testRegionReplicasOnSmallCluster() { 196 int numNodes = 10; 197 int numRegions = 1000; 198 int replication = 3; // 3 replicas per region 199 int numRegionsPerServer = 80; // all regions are mostly balanced 200 int numTables = 10; 201 testWithClusterWithIteration(numNodes, numRegions, numRegionsPerServer, replication, numTables, 202 true, true); 203 } 204 205 private static class ForTestRackManagerOne extends RackManager { 206 @Override 207 public String getRack(ServerName server) { 208 return server.getHostname().endsWith("1") ? "rack1" : "rack2"; 209 } 210 } 211}