1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.hadoop.hbase.master.balancer;
21
22 import static org.junit.Assert.assertEquals;
23 import static org.junit.Assert.assertFalse;
24 import static org.junit.Assert.assertTrue;
25
26 import com.google.common.collect.ArrayListMultimap;
27 import com.google.common.collect.Lists;
28
29 import java.io.FileNotFoundException;
30 import java.io.IOException;
31 import java.security.SecureRandom;
32 import java.util.ArrayList;
33 import java.util.HashMap;
34 import java.util.List;
35 import java.util.Map;
36 import java.util.Set;
37 import java.util.TreeMap;
38 import java.util.TreeSet;
39
40 import org.apache.commons.lang.StringUtils;
41 import org.apache.hadoop.hbase.HRegionInfo;
42 import org.apache.hadoop.hbase.HTableDescriptor;
43 import org.apache.hadoop.hbase.ServerName;
44 import org.apache.hadoop.hbase.TableDescriptors;
45 import org.apache.hadoop.hbase.TableName;
46 import org.apache.hadoop.hbase.master.AssignmentManager;
47 import org.apache.hadoop.hbase.master.HMaster;
48 import org.apache.hadoop.hbase.master.MasterServices;
49 import org.apache.hadoop.hbase.master.RegionPlan;
50 import org.apache.hadoop.hbase.net.Address;
51 import org.apache.hadoop.hbase.rsgroup.RSGroupInfo;
52 import org.apache.hadoop.hbase.rsgroup.RSGroupInfoManager;
53 import org.apache.hadoop.hbase.util.Bytes;
54 import org.mockito.Mockito;
55 import org.mockito.invocation.InvocationOnMock;
56 import org.mockito.stubbing.Answer;
57
58 public class RSGroupableBalancerTestBase {
59
60 static SecureRandom rand = new SecureRandom();
61 static String[] groups = new String[] { RSGroupInfo.DEFAULT_GROUP, "dg2", "dg3", "dg4" };
62 static TableName table0 = TableName.valueOf("dt0");
63 static TableName[] tables =
64 new TableName[] { TableName.valueOf("dt1"),
65 TableName.valueOf("dt2"),
66 TableName.valueOf("dt3"),
67 TableName.valueOf("dt4")};
68 static List<ServerName> servers;
69 static Map<String, RSGroupInfo> groupMap;
70 static Map<TableName, String> tableMap = new HashMap<>();
71 static List<HTableDescriptor> tableDescs;
72 int[] regionAssignment = new int[] { 2, 5, 7, 10, 4, 3, 1 };
73 static int regionId = 0;
74
75
76
77
78
79 protected void assertClusterAsBalanced(
80 ArrayListMultimap<String, ServerAndLoad> groupLoadMap) {
81 for (String gName : groupLoadMap.keySet()) {
82 List<ServerAndLoad> groupLoad = groupLoadMap.get(gName);
83 int numServers = groupLoad.size();
84 int numRegions = 0;
85 int maxRegions = 0;
86 int minRegions = Integer.MAX_VALUE;
87 for (ServerAndLoad server : groupLoad) {
88 int nr = server.getLoad();
89 if (nr > maxRegions) {
90 maxRegions = nr;
91 }
92 if (nr < minRegions) {
93 minRegions = nr;
94 }
95 numRegions += nr;
96 }
97 if (maxRegions - minRegions < 2) {
98
99 return;
100 }
101 int min = numRegions / numServers;
102 int max = numRegions % numServers == 0 ? min : min + 1;
103
104 for (ServerAndLoad server : groupLoad) {
105 assertTrue(server.getLoad() <= max);
106 assertTrue(server.getLoad() >= min);
107 }
108 }
109 }
110
111
112
113
114
115
116
117
118
119
120
121 protected void assertRetainedAssignment(
122 Map<HRegionInfo, ServerName> existing, List<ServerName> servers,
123 Map<ServerName, List<HRegionInfo>> assignment)
124 throws FileNotFoundException, IOException {
125
126 Set<ServerName> onlineServerSet = new TreeSet<ServerName>(servers);
127 Set<HRegionInfo> assignedRegions = new TreeSet<HRegionInfo>();
128 for (Map.Entry<ServerName, List<HRegionInfo>> a : assignment.entrySet()) {
129 assertTrue(
130 "Region assigned to server that was not listed as online",
131 onlineServerSet.contains(a.getKey()));
132 for (HRegionInfo r : a.getValue()) {
133 assignedRegions.add(r);
134 }
135 }
136 assertEquals(existing.size(), assignedRegions.size());
137
138
139 Set<String> onlineHostNames = new TreeSet<String>();
140 for (ServerName s : servers) {
141 onlineHostNames.add(s.getHostname());
142 }
143
144 for (Map.Entry<ServerName, List<HRegionInfo>> a : assignment.entrySet()) {
145 ServerName currentServer = a.getKey();
146 for (HRegionInfo r : a.getValue()) {
147 ServerName oldAssignedServer = existing.get(r);
148 TableName tableName = r.getTable();
149 String groupName =
150 getMockedGroupInfoManager().getRSGroupOfTable(tableName);
151 assertTrue(StringUtils.isNotEmpty(groupName));
152 RSGroupInfo gInfo = getMockedGroupInfoManager().getRSGroup(
153 groupName);
154 assertTrue(
155 "Region is not correctly assigned to group servers.",
156 gInfo.containsServer(currentServer.getAddress()));
157 if (oldAssignedServer != null
158 && onlineHostNames.contains(oldAssignedServer
159 .getHostname())) {
160
161
162
163 if (!oldAssignedServer.getAddress().equals(currentServer.getAddress())) {
164 assertFalse(gInfo.containsServer(oldAssignedServer.getAddress()));
165 }
166 }
167 }
168 }
169 }
170
171 protected String printStats(
172 ArrayListMultimap<String, ServerAndLoad> groupBasedLoad) {
173 StringBuffer sb = new StringBuffer();
174 sb.append("\n");
175 for (String groupName : groupBasedLoad.keySet()) {
176 sb.append("Stats for group: " + groupName);
177 sb.append("\n");
178 sb.append(groupMap.get(groupName).getServers());
179 sb.append("\n");
180 List<ServerAndLoad> groupLoad = groupBasedLoad.get(groupName);
181 int numServers = groupLoad.size();
182 int totalRegions = 0;
183 sb.append("Per Server Load: \n");
184 for (ServerAndLoad sLoad : groupLoad) {
185 sb.append("Server :" + sLoad.getServerName() + " Load : "
186 + sLoad.getLoad() + "\n");
187 totalRegions += sLoad.getLoad();
188 }
189 sb.append(" Group Statistics : \n");
190 float average = (float) totalRegions / numServers;
191 int max = (int) Math.ceil(average);
192 int min = (int) Math.floor(average);
193 sb.append("[srvr=" + numServers + " rgns=" + totalRegions + " avg="
194 + average + " max=" + max + " min=" + min + "]");
195 sb.append("\n");
196 sb.append("===============================");
197 sb.append("\n");
198 }
199 return sb.toString();
200 }
201
202 protected ArrayListMultimap<String, ServerAndLoad> convertToGroupBasedMap(
203 final Map<ServerName, List<HRegionInfo>> serversMap) throws IOException {
204 ArrayListMultimap<String, ServerAndLoad> loadMap = ArrayListMultimap
205 .create();
206 for (RSGroupInfo gInfo : getMockedGroupInfoManager().listRSGroups()) {
207 Set<Address> groupServers = gInfo.getServers();
208 for (Address server : groupServers) {
209 ServerName actual = null;
210 for(ServerName entry: servers) {
211 if(entry.getAddress().equals(server)) {
212 actual = entry;
213 break;
214 }
215 }
216 List<HRegionInfo> regions = serversMap.get(actual);
217 assertTrue("No load for " + actual, regions != null);
218 loadMap.put(gInfo.getName(),
219 new ServerAndLoad(actual, regions.size()));
220 }
221 }
222 return loadMap;
223 }
224
225 protected ArrayListMultimap<String, ServerAndLoad> reconcile(
226 ArrayListMultimap<String, ServerAndLoad> previousLoad,
227 List<RegionPlan> plans) {
228 ArrayListMultimap<String, ServerAndLoad> result = ArrayListMultimap
229 .create();
230 result.putAll(previousLoad);
231 if (plans != null) {
232 for (RegionPlan plan : plans) {
233 ServerName source = plan.getSource();
234 updateLoad(result, source, -1);
235 ServerName destination = plan.getDestination();
236 updateLoad(result, destination, +1);
237 }
238 }
239 return result;
240 }
241
242 protected void updateLoad(
243 ArrayListMultimap<String, ServerAndLoad> previousLoad,
244 final ServerName sn, final int diff) {
245 for (String groupName : previousLoad.keySet()) {
246 ServerAndLoad newSAL = null;
247 ServerAndLoad oldSAL = null;
248 for (ServerAndLoad sal : previousLoad.get(groupName)) {
249 if (ServerName.isSameAddress(sn, sal.getServerName())) {
250 oldSAL = sal;
251 newSAL = new ServerAndLoad(sn, sal.getLoad() + diff);
252 break;
253 }
254 }
255 if (newSAL != null) {
256 previousLoad.remove(groupName, oldSAL);
257 previousLoad.put(groupName, newSAL);
258 break;
259 }
260 }
261 }
262
263 protected Map<ServerName, List<HRegionInfo>> mockClusterServers() throws IOException {
264 assertTrue(servers.size() == regionAssignment.length);
265 Map<ServerName, List<HRegionInfo>> assignment = new TreeMap<ServerName, List<HRegionInfo>>();
266 for (int i = 0; i < servers.size(); i++) {
267 int numRegions = regionAssignment[i];
268 List<HRegionInfo> regions = assignedRegions(numRegions, servers.get(i));
269 assignment.put(servers.get(i), regions);
270 }
271 return assignment;
272 }
273
274
275
276
277
278
279
280 protected List<HRegionInfo> randomRegions(int numRegions) {
281 List<HRegionInfo> regions = new ArrayList<HRegionInfo>(numRegions);
282 byte[] start = new byte[16];
283 byte[] end = new byte[16];
284 rand.nextBytes(start);
285 rand.nextBytes(end);
286 int regionIdx = rand.nextInt(tables.length);
287 for (int i = 0; i < numRegions; i++) {
288 Bytes.putInt(start, 0, numRegions << 1);
289 Bytes.putInt(end, 0, (numRegions << 1) + 1);
290 int tableIndex = (i + regionIdx) % tables.length;
291 HRegionInfo hri = new HRegionInfo(
292 tables[tableIndex], start, end, false, regionId++);
293 regions.add(hri);
294 }
295 return regions;
296 }
297
298
299
300
301
302
303
304
305
306 protected List<HRegionInfo> assignedRegions(int numRegions, ServerName sn) throws IOException {
307 List<HRegionInfo> regions = new ArrayList<HRegionInfo>(numRegions);
308 byte[] start = new byte[16];
309 byte[] end = new byte[16];
310 Bytes.putInt(start, 0, numRegions << 1);
311 Bytes.putInt(end, 0, (numRegions << 1) + 1);
312 for (int i = 0; i < numRegions; i++) {
313 TableName tableName = getTableName(sn);
314 HRegionInfo hri = new HRegionInfo(
315 tableName, start, end, false,
316 regionId++);
317 regions.add(hri);
318 }
319 return regions;
320 }
321
322 protected static List<ServerName> generateServers(int numServers) {
323 List<ServerName> servers = new ArrayList<ServerName>(numServers);
324 for (int i = 0; i < numServers; i++) {
325 String host = "server" + rand.nextInt(100000);
326 int port = rand.nextInt(60000);
327 servers.add(ServerName.valueOf(host, port, -1));
328 }
329 return servers;
330 }
331
332
333
334
335
336
337
338
339 protected static Map<String, RSGroupInfo> constructGroupInfo(
340 List<ServerName> servers, String[] groups) {
341 assertTrue(servers != null);
342 assertTrue(servers.size() >= groups.length);
343 int index = 0;
344 Map<String, RSGroupInfo> groupMap = new HashMap<String, RSGroupInfo>();
345 for (String grpName : groups) {
346 RSGroupInfo RSGroupInfo = new RSGroupInfo(grpName);
347 RSGroupInfo.addServer(servers.get(index).getAddress());
348 groupMap.put(grpName, RSGroupInfo);
349 index++;
350 }
351 while (index < servers.size()) {
352 int grpIndex = rand.nextInt(groups.length);
353 groupMap.get(groups[grpIndex]).addServer(servers.get(index).getAddress());
354 index++;
355 }
356 return groupMap;
357 }
358
359
360
361
362
363
364 protected static List<HTableDescriptor> constructTableDesc(boolean hasBogusTable) {
365 List<HTableDescriptor> tds = Lists.newArrayList();
366 int index = rand.nextInt(groups.length);
367 for (int i = 0; i < tables.length; i++) {
368 HTableDescriptor htd = new HTableDescriptor(tables[i]);
369 int grpIndex = (i + index) % groups.length ;
370 String groupName = groups[grpIndex];
371 tableMap.put(tables[i], groupName);
372 tds.add(htd);
373 }
374 if (hasBogusTable) {
375 tableMap.put(table0, "");
376 tds.add(new HTableDescriptor(table0));
377 }
378 return tds;
379 }
380
381 protected static MasterServices getMockedMaster() throws IOException {
382 TableDescriptors tds = Mockito.mock(TableDescriptors.class);
383 Mockito.when(tds.get(tables[0])).thenReturn(tableDescs.get(0));
384 Mockito.when(tds.get(tables[1])).thenReturn(tableDescs.get(1));
385 Mockito.when(tds.get(tables[2])).thenReturn(tableDescs.get(2));
386 Mockito.when(tds.get(tables[3])).thenReturn(tableDescs.get(3));
387 MasterServices services = Mockito.mock(HMaster.class);
388 Mockito.when(services.getTableDescriptors()).thenReturn(tds);
389 AssignmentManager am = Mockito.mock(AssignmentManager.class);
390 Mockito.when(services.getAssignmentManager()).thenReturn(am);
391 return services;
392 }
393
394 protected static RSGroupInfoManager getMockedGroupInfoManager() throws IOException {
395 RSGroupInfoManager gm = Mockito.mock(RSGroupInfoManager.class);
396 Mockito.when(gm.getRSGroup(Mockito.anyString())).thenAnswer(new Answer<RSGroupInfo>() {
397 @Override
398 public RSGroupInfo answer(InvocationOnMock invocation) throws Throwable {
399 return groupMap.get(invocation.getArguments()[0]);
400 }
401 });
402 Mockito.when(gm.listRSGroups()).thenReturn(
403 Lists.newLinkedList(groupMap.values()));
404 Mockito.when(gm.isOnline()).thenReturn(true);
405 Mockito.when(gm.getRSGroupOfTable(Mockito.any(TableName.class)))
406 .thenAnswer(new Answer<String>() {
407 @Override
408 public String answer(InvocationOnMock invocation) throws Throwable {
409 return tableMap.get(invocation.getArguments()[0]);
410 }
411 });
412 return gm;
413 }
414
415 protected TableName getTableName(ServerName sn) throws IOException {
416 TableName tableName = null;
417 RSGroupInfoManager gm = getMockedGroupInfoManager();
418 RSGroupInfo groupOfServer = null;
419 for(RSGroupInfo gInfo : gm.listRSGroups()){
420 if(gInfo.containsServer(sn.getAddress())){
421 groupOfServer = gInfo;
422 break;
423 }
424 }
425
426 for(HTableDescriptor desc : tableDescs){
427 if(gm.getRSGroupOfTable(desc.getTableName()).endsWith(groupOfServer.getName())){
428 tableName = desc.getTableName();
429 }
430 }
431 return tableName;
432 }
433 }