1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.hadoop.hbase.master.balancer;
19
20 import static org.junit.Assert.assertTrue;
21
22 import java.util.ArrayList;
23 import java.util.HashMap;
24 import java.util.HashSet;
25 import java.util.LinkedList;
26 import java.util.List;
27 import java.util.Map;
28 import java.util.Map.Entry;
29 import java.util.Queue;
30 import java.util.Random;
31 import java.util.Set;
32 import java.util.SortedSet;
33 import java.util.TreeMap;
34 import java.util.TreeSet;
35
36 import org.apache.hadoop.conf.Configuration;
37 import org.apache.hadoop.hbase.HRegionInfo;
38 import org.apache.hadoop.hbase.ServerName;
39 import org.apache.hadoop.hbase.TableName;
40 import org.apache.hadoop.hbase.client.RegionReplicaUtil;
41 import org.apache.hadoop.hbase.master.RackManager;
42 import org.apache.hadoop.hbase.master.RegionPlan;
43 import org.apache.hadoop.hbase.util.Bytes;
44 import org.apache.hadoop.net.DNSToSwitchMapping;
45 import org.junit.Assert;
46
47
48
49
50
51
52
53 public class BalancerTestBase {
54
55 protected static Random rand = new Random();
56 static int regionId = 0;
57
58
59 public static class MockMapping implements DNSToSwitchMapping {
60 public MockMapping(Configuration conf) {
61 }
62
63 public List<String> resolve(List<String> names) {
64 List<String> ret = new ArrayList<String>(names.size());
65 for (String name : names) {
66 ret.add("rack");
67 }
68 return ret;
69 }
70
71
72 public void reloadCachedMappings() {
73 }
74
75
76 public void reloadCachedMappings(List<String> arg0) {
77 }
78 }
79
80
81
82
83
84 public void assertClusterAsBalanced(List<ServerAndLoad> servers) {
85 int numServers = servers.size();
86 int numRegions = 0;
87 int maxRegions = 0;
88 int minRegions = Integer.MAX_VALUE;
89 for (ServerAndLoad server : servers) {
90 int nr = server.getLoad();
91 if (nr > maxRegions) {
92 maxRegions = nr;
93 }
94 if (nr < minRegions) {
95 minRegions = nr;
96 }
97 numRegions += nr;
98 }
99 if (maxRegions - minRegions < 2) {
100
101 return;
102 }
103 int min = numRegions / numServers;
104 int max = numRegions % numServers == 0 ? min : min + 1;
105
106 for (ServerAndLoad server : servers) {
107 assertTrue(server.getLoad() >= 0);
108 assertTrue(server.getLoad() <= max);
109 assertTrue(server.getLoad() >= min);
110 }
111 }
112
113
114
115
116 public void assertRegionReplicaPlacement(Map<ServerName, List<HRegionInfo>> serverMap, RackManager rackManager) {
117 TreeMap<String, Set<HRegionInfo>> regionsPerHost = new TreeMap<String, Set<HRegionInfo>>();
118 TreeMap<String, Set<HRegionInfo>> regionsPerRack = new TreeMap<String, Set<HRegionInfo>>();
119
120 for (Entry<ServerName, List<HRegionInfo>> entry : serverMap.entrySet()) {
121 String hostname = entry.getKey().getHostname();
122 Set<HRegionInfo> infos = regionsPerHost.get(hostname);
123 if (infos == null) {
124 infos = new HashSet<HRegionInfo>();
125 regionsPerHost.put(hostname, infos);
126 }
127
128 for (HRegionInfo info : entry.getValue()) {
129 HRegionInfo primaryInfo = RegionReplicaUtil.getRegionInfoForDefaultReplica(info);
130 if (!infos.add(primaryInfo)) {
131 Assert.fail("Two or more region replicas are hosted on the same host after balance");
132 }
133 }
134 }
135
136 if (rackManager == null) {
137 return;
138 }
139
140 for (Entry<ServerName, List<HRegionInfo>> entry : serverMap.entrySet()) {
141 String rack = rackManager.getRack(entry.getKey());
142 Set<HRegionInfo> infos = regionsPerRack.get(rack);
143 if (infos == null) {
144 infos = new HashSet<HRegionInfo>();
145 regionsPerRack.put(rack, infos);
146 }
147
148 for (HRegionInfo info : entry.getValue()) {
149 HRegionInfo primaryInfo = RegionReplicaUtil.getRegionInfoForDefaultReplica(info);
150 if (!infos.add(primaryInfo)) {
151 Assert.fail("Two or more region replicas are hosted on the same rack after balance");
152 }
153 }
154 }
155 }
156
157 protected String printStats(List<ServerAndLoad> servers) {
158 int numServers = servers.size();
159 int totalRegions = 0;
160 for (ServerAndLoad server : servers) {
161 totalRegions += server.getLoad();
162 }
163 float average = (float) totalRegions / numServers;
164 int max = (int) Math.ceil(average);
165 int min = (int) Math.floor(average);
166 return "[srvr=" + numServers + " rgns=" + totalRegions + " avg=" + average + " max=" + max
167 + " min=" + min + "]";
168 }
169
170 protected List<ServerAndLoad> convertToList(final Map<ServerName, List<HRegionInfo>> servers) {
171 List<ServerAndLoad> list = new ArrayList<ServerAndLoad>(servers.size());
172 for (Map.Entry<ServerName, List<HRegionInfo>> e : servers.entrySet()) {
173 list.add(new ServerAndLoad(e.getKey(), e.getValue().size()));
174 }
175 return list;
176 }
177
178 protected String printMock(List<ServerAndLoad> balancedCluster) {
179 SortedSet<ServerAndLoad> sorted = new TreeSet<ServerAndLoad>(balancedCluster);
180 ServerAndLoad[] arr = sorted.toArray(new ServerAndLoad[sorted.size()]);
181 StringBuilder sb = new StringBuilder(sorted.size() * 4 + 4);
182 sb.append("{ ");
183 for (int i = 0; i < arr.length; i++) {
184 if (i != 0) {
185 sb.append(" , ");
186 }
187 sb.append(arr[i].getServerName().getHostname());
188 sb.append(":");
189 sb.append(arr[i].getLoad());
190 }
191 sb.append(" }");
192 return sb.toString();
193 }
194
195
196
197
198
199
200
201
202
203 protected List<ServerAndLoad> reconcile(List<ServerAndLoad> list,
204 List<RegionPlan> plans,
205 Map<ServerName, List<HRegionInfo>> servers) {
206 List<ServerAndLoad> result = new ArrayList<ServerAndLoad>(list.size());
207
208 Map<ServerName, ServerAndLoad> map = new HashMap<ServerName, ServerAndLoad>(list.size());
209 for (ServerAndLoad sl : list) {
210 map.put(sl.getServerName(), sl);
211 }
212 if (plans != null) {
213 for (RegionPlan plan : plans) {
214 ServerName source = plan.getSource();
215
216 updateLoad(map, source, -1);
217 ServerName destination = plan.getDestination();
218 updateLoad(map, destination, +1);
219
220 servers.get(source).remove(plan.getRegionInfo());
221 servers.get(destination).add(plan.getRegionInfo());
222 }
223 }
224 result.clear();
225 result.addAll(map.values());
226 return result;
227 }
228
229 protected void updateLoad(final Map<ServerName, ServerAndLoad> map,
230 final ServerName sn,
231 final int diff) {
232 ServerAndLoad sal = map.get(sn);
233 if (sal == null) sal = new ServerAndLoad(sn, 0);
234 sal = new ServerAndLoad(sn, sal.getLoad() + diff);
235 map.put(sn, sal);
236 }
237
238 protected TreeMap<ServerName, List<HRegionInfo>> mockClusterServers(int[] mockCluster) {
239 return mockClusterServers(mockCluster, -1);
240 }
241
242 protected BaseLoadBalancer.Cluster mockCluster(int[] mockCluster) {
243 return new BaseLoadBalancer.Cluster(
244 mockClusterServers(mockCluster, -1), null, null, null);
245 }
246
247 protected TreeMap<ServerName, List<HRegionInfo>> mockClusterServers(int[] mockCluster, int numTables) {
248 int numServers = mockCluster.length;
249 TreeMap<ServerName, List<HRegionInfo>> servers = new TreeMap<ServerName, List<HRegionInfo>>();
250 for (int i = 0; i < numServers; i++) {
251 int numRegions = mockCluster[i];
252 ServerAndLoad sal = randomServer(0);
253 List<HRegionInfo> regions = randomRegions(numRegions, numTables);
254 servers.put(sal.getServerName(), regions);
255 }
256 return servers;
257 }
258
259 private Queue<HRegionInfo> regionQueue = new LinkedList<HRegionInfo>();
260
261 protected List<HRegionInfo> randomRegions(int numRegions) {
262 return randomRegions(numRegions, -1);
263 }
264
265 protected List<HRegionInfo> randomRegions(int numRegions, int numTables) {
266 List<HRegionInfo> regions = new ArrayList<HRegionInfo>(numRegions);
267 byte[] start = new byte[16];
268 byte[] end = new byte[16];
269 rand.nextBytes(start);
270 rand.nextBytes(end);
271 for (int i = 0; i < numRegions; i++) {
272 if (!regionQueue.isEmpty()) {
273 regions.add(regionQueue.poll());
274 continue;
275 }
276 Bytes.putInt(start, 0, numRegions << 1);
277 Bytes.putInt(end, 0, (numRegions << 1) + 1);
278 TableName tableName =
279 TableName.valueOf("table" + (numTables > 0 ? rand.nextInt(numTables) : i));
280 HRegionInfo hri = new HRegionInfo(tableName, start, end, false, regionId++);
281 regions.add(hri);
282 }
283 return regions;
284 }
285
286 protected void returnRegions(List<HRegionInfo> regions) {
287 regionQueue.addAll(regions);
288 }
289
290 private Queue<ServerName> serverQueue = new LinkedList<ServerName>();
291
292 protected ServerAndLoad randomServer(final int numRegionsPerServer) {
293 if (!this.serverQueue.isEmpty()) {
294 ServerName sn = this.serverQueue.poll();
295 return new ServerAndLoad(sn, numRegionsPerServer);
296 }
297 String host = "srv" + rand.nextInt(Integer.MAX_VALUE);
298 int port = rand.nextInt(60000);
299 long startCode = rand.nextLong();
300 ServerName sn = ServerName.valueOf(host, port, startCode);
301 return new ServerAndLoad(sn, numRegionsPerServer);
302 }
303
304 protected List<ServerAndLoad> randomServers(int numServers, int numRegionsPerServer) {
305 List<ServerAndLoad> servers = new ArrayList<ServerAndLoad>(numServers);
306 for (int i = 0; i < numServers; i++) {
307 servers.add(randomServer(numRegionsPerServer));
308 }
309 return servers;
310 }
311
312 protected void returnServer(ServerName server) {
313 serverQueue.add(server);
314 }
315
316 protected void returnServers(List<ServerName> servers) {
317 this.serverQueue.addAll(servers);
318 }
319
320 }