1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package org.apache.hadoop.hbase.master.balancer;
16
17 import static junit.framework.TestCase.assertNotNull;
18 import static junit.framework.TestCase.assertTrue;
19 import static org.junit.Assert.assertNull;
20
21 import java.io.IOException;
22 import java.util.Arrays;
23 import java.util.Collections;
24 import java.util.LinkedList;
25 import java.util.List;
26 import java.util.Map;
27 import java.util.Queue;
28 import java.util.Random;
29 import java.util.TreeMap;
30 import java.util.concurrent.ThreadLocalRandom;
31
32 import org.apache.hadoop.conf.Configuration;
33 import org.apache.hadoop.hbase.HRegionInfo;
34 import org.apache.hadoop.hbase.ServerName;
35 import org.apache.hadoop.hbase.client.RegionReplicaUtil;
36 import org.apache.hadoop.hbase.master.RackManager;
37 import org.apache.hadoop.hbase.master.RegionPlan;
38 import org.apache.hadoop.hbase.testclassification.MediumTests;
39 import org.junit.BeforeClass;
40 import org.junit.Test;
41 import org.junit.experimental.categories.Category;
42 import org.slf4j.Logger;
43 import org.slf4j.LoggerFactory;
44
45 @Category({MediumTests.class})
46 public class TestStochasticLoadBalancerHeterogeneousCost extends BalancerTestBase {
47
48 private static final Logger LOG =
49 LoggerFactory.getLogger(TestStochasticLoadBalancerHeterogeneousCost.class);
50 private static final double allowedWindow = 1.20;
51
52 @BeforeClass
53 public static void beforeAllTests() {
54 conf = new Configuration();
55 conf.setFloat("hbase.master.balancer.stochastic.regionCountCost", 0);
56 conf.setFloat("hbase.master.balancer.stochastic.primaryRegionCountCost", 0);
57 conf.setFloat("hbase.master.balancer.stochastic.tableSkewCost", 0);
58 conf.setBoolean("hbase.master.balancer.stochastic.runMaxSteps", true);
59 conf.set(StochasticLoadBalancer.COST_FUNCTIONS_COST_FUNCTIONS_KEY,
60 HeterogeneousRegionCountCostFunction.class.getName());
61
62 conf.set(
63 HeterogeneousRegionCountCostFunction.HBASE_MASTER_BALANCER_HETEROGENEOUS_RULES_FILE,
64 TestStochasticLoadBalancerHeterogeneousCostRules.DEFAULT_RULES_TMP_LOCATION);
65 loadBalancer = new StochasticLoadBalancer();
66 loadBalancer.setConf(conf);
67 }
68
69 @Test
70 public void testDefault() throws IOException {
71 final List<String> rules = Collections.emptyList();
72
73 final int numNodes = 2;
74 final int numRegions = 300;
75 final int numRegionsPerServer = 250;
76
77
78
79
80 this.testHeterogeneousWithCluster(numNodes, numRegions, numRegionsPerServer, rules);
81 }
82
83 @Test
84 public void testOneGroup() throws IOException {
85 final List<String> rules = Collections.singletonList("rs.* 100");
86
87 final int numNodes = 4;
88 final int numRegions = 300;
89 final int numRegionsPerServer = 30;
90
91
92
93
94 this.testHeterogeneousWithCluster(numNodes, numRegions, numRegionsPerServer, rules);
95 }
96
97 @Test
98 public void testTwoGroups() throws IOException {
99 final List<String> rules = Arrays.asList("rs[0-4] 200", "rs[5-9] 50");
100
101 final int numNodes = 10;
102 final int numRegions = 500;
103 final int numRegionsPerServer = 50;
104
105
106
107
108
109
110 this.testHeterogeneousWithCluster(numNodes, numRegions, numRegionsPerServer, rules);
111 }
112
113 @Test
114 public void testFourGroups() throws IOException {
115 final List<String> rules = Arrays.asList("rs[1-3] 200", "rs[4-7] 250", "rs[8-9] 100");
116
117 final int numNodes = 10;
118 final int numRegions = 800;
119 final int numRegionsPerServer = 80;
120
121
122
123
124
125
126 this.testHeterogeneousWithCluster(numNodes, numRegions, numRegionsPerServer, rules);
127 }
128
129 @Test
130 public void testOverloaded() throws IOException {
131 final List<String> rules = Collections.singletonList("rs[0-1] 50");
132
133 final int numNodes = 2;
134 final int numRegions = 120;
135 final int numRegionsPerServer = 60;
136
137 TestStochasticLoadBalancerHeterogeneousCostRules.createSimpleRulesFile(rules);
138 final Map<ServerName, List<HRegionInfo>> serverMap =
139 this.createServerMap(numNodes, numRegions, numRegionsPerServer, 1, 1);
140 final List<RegionPlan> plans = BalancerTestBase.loadBalancer.balanceCluster(serverMap);
141
142
143 assertNull(plans);
144 }
145
146 private void testHeterogeneousWithCluster(final int numNodes, final int numRegions,
147 final int numRegionsPerServer, final List<String> rules) throws IOException {
148
149 TestStochasticLoadBalancerHeterogeneousCostRules.createSimpleRulesFile(rules);
150 final Map<ServerName, List<HRegionInfo>> serverMap =
151 this.createServerMap(numNodes, numRegions, numRegionsPerServer, 1, 1);
152 this.testWithCluster(serverMap, null, true, false);
153 }
154
155 protected void testWithCluster(final Map<ServerName, List<HRegionInfo>> serverMap,
156 final RackManager rackManager, final boolean assertFullyBalanced,
157 final boolean assertFullyBalancedForReplicas) {
158 final List<ServerAndLoad> list = this.convertToList(serverMap);
159 LOG.info("Mock Cluster : " + this.printMock(list) + " " + this.printStats(list));
160
161 BalancerTestBase.loadBalancer.setRackManager(rackManager);
162
163
164 final List<RegionPlan> plans = BalancerTestBase.loadBalancer.balanceCluster(serverMap);
165 assertNotNull(plans);
166
167
168 if (assertFullyBalanced || assertFullyBalancedForReplicas) {
169
170 final List<ServerAndLoad> balancedCluster = this.reconcile(list, plans, serverMap);
171
172
173 LOG.info("Mock Balanced cluster : " + this.printMock(balancedCluster));
174
175 if (assertFullyBalanced) {
176 final List<RegionPlan> secondPlans =
177 BalancerTestBase.loadBalancer.balanceCluster(serverMap);
178 assertNull(secondPlans);
179
180
181
182 final HeterogeneousRegionCountCostFunction cf =
183 new HeterogeneousRegionCountCostFunction(conf);
184 assertNotNull(cf);
185 BaseLoadBalancer.Cluster cluster =
186 new BaseLoadBalancer.Cluster(serverMap, null, null, null);
187 cf.init(cluster);
188
189
190 for (final ServerAndLoad serverAndLoad : balancedCluster) {
191 final ServerName sn = serverAndLoad.getServerName();
192 final int numberRegions = serverAndLoad.getLoad();
193 final int limit = cf.findLimitForRS(sn);
194
195 double usage = (double) numberRegions / (double) limit;
196 LOG.debug(
197 sn.getHostname() + ":" + numberRegions + "/" + limit + "(" + (usage * 100) + "%)");
198
199
200
201 assertTrue("Host " + sn.getHostname() + " should be below "
202 + cf.overallUsage * allowedWindow * 100 + "%",
203 usage <= cf.overallUsage * allowedWindow);
204 }
205 }
206
207 if (assertFullyBalancedForReplicas) {
208 this.assertRegionReplicaPlacement(serverMap, rackManager);
209 }
210 }
211 }
212
213 @Override
214 protected Map<ServerName, List<HRegionInfo>> createServerMap(int numNodes, int numRegions,
215 int numRegionsPerServer, int replication, int numTables) {
216
217
218
219 int[] cluster = new int[numNodes];
220 for (int i = 0; i < numNodes; i++) {
221 cluster[i] = numRegionsPerServer;
222 }
223 cluster[cluster.length - 1] = numRegions - ((cluster.length - 1) * numRegionsPerServer);
224 Map<ServerName, List<HRegionInfo>> clusterState = mockClusterServers(cluster, numTables);
225 if (replication > 0) {
226
227 for (List<HRegionInfo> regions : clusterState.values()) {
228 int length = regions.size();
229 for (int i = 0; i < length; i++) {
230 for (int r = 1; r < replication; r++) {
231 regions.add(RegionReplicaUtil.getRegionInfoForReplica(regions.get(i), r));
232 }
233 }
234 }
235 }
236
237 return clusterState;
238 }
239
240 @Override
241 protected TreeMap<ServerName, List<HRegionInfo>> mockClusterServers(int[] mockCluster,
242 int numTables) {
243 int numServers = mockCluster.length;
244 TreeMap<ServerName, List<HRegionInfo>> servers = new TreeMap<>();
245 for (int i = 0; i < numServers; i++) {
246 int numRegions = mockCluster[i];
247 ServerAndLoad sal = createServer("rs" + i);
248 List<HRegionInfo> regions = randomRegions(numRegions, numTables);
249 servers.put(sal.getServerName(), regions);
250 }
251 return servers;
252 }
253
254 private Queue<ServerName> serverQueue = new LinkedList<>();
255
256 private ServerAndLoad createServer(final String host) {
257 if (!this.serverQueue.isEmpty()) {
258 ServerName sn = this.serverQueue.poll();
259 return new ServerAndLoad(sn, 0);
260 }
261 Random rand = ThreadLocalRandom.current();
262 int port = rand.nextInt(60000);
263 long startCode = rand.nextLong();
264 ServerName sn = ServerName.valueOf(host, port, startCode);
265 return new ServerAndLoad(sn, 0);
266 }
267 }