1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package org.apache.hadoop.hbase.rsgroup;
22
23 import com.google.common.collect.ArrayListMultimap;
24 import com.google.common.collect.LinkedListMultimap;
25 import com.google.common.collect.ListMultimap;
26 import com.google.common.collect.Lists;
27 import com.google.common.collect.Maps;
28
29 import java.io.IOException;
30 import java.util.ArrayList;
31 import java.util.Collections;
32 import java.util.HashMap;
33 import java.util.HashSet;
34 import java.util.LinkedList;
35 import java.util.List;
36 import java.util.Map;
37 import java.util.Set;
38 import java.util.TreeMap;
39
40 import org.apache.commons.logging.Log;
41 import org.apache.commons.logging.LogFactory;
42 import org.apache.hadoop.conf.Configuration;
43 import org.apache.hadoop.hbase.ClusterStatus;
44 import org.apache.hadoop.hbase.HBaseIOException;
45 import org.apache.hadoop.hbase.HRegionInfo;
46 import org.apache.hadoop.hbase.ServerName;
47 import org.apache.hadoop.hbase.TableName;
48 import org.apache.hadoop.hbase.classification.InterfaceAudience;
49 import org.apache.hadoop.hbase.constraint.ConstraintException;
50 import org.apache.hadoop.hbase.master.LoadBalancer;
51 import org.apache.hadoop.hbase.master.MasterServices;
52 import org.apache.hadoop.hbase.master.RegionPlan;
53 import org.apache.hadoop.hbase.master.balancer.StochasticLoadBalancer;
54 import org.apache.hadoop.hbase.net.Address;
55 import org.apache.hadoop.util.ReflectionUtils;
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71 @InterfaceAudience.Private
72 public class RSGroupBasedLoadBalancer implements RSGroupableBalancer, LoadBalancer {
73
74 public static final String HBASE_GROUP_LOADBALANCER_CLASS = "hbase.group.grouploadbalancer.class";
75
76 private static final Log LOG = LogFactory.getLog(RSGroupBasedLoadBalancer.class);
77
78 private Configuration config;
79 private ClusterStatus clusterStatus;
80 private MasterServices masterServices;
81 private RSGroupInfoManager infoManager;
82 private LoadBalancer internalBalancer;
83
84
85 @InterfaceAudience.Private
86 public RSGroupBasedLoadBalancer() {
87 }
88
89
90 @InterfaceAudience.Private
91 public RSGroupBasedLoadBalancer(RSGroupInfoManager RSGroupInfoManager) {
92 this.infoManager = RSGroupInfoManager;
93 }
94
95 @Override
96 public Configuration getConf() {
97 return config;
98 }
99
100 @Override
101 public void setConf(Configuration conf) {
102 this.config = conf;
103 if (internalBalancer != null) {
104 internalBalancer.setConf(conf);
105 }
106 }
107
108 @Override
109 public void setClusterStatus(ClusterStatus st) {
110 this.clusterStatus = st;
111 if (internalBalancer != null) {
112 internalBalancer.setClusterStatus(st);
113 }
114 }
115
116 @Override
117 public void setMasterServices(MasterServices masterServices) {
118 this.masterServices = masterServices;
119 }
120
121 @Override
122 public List<RegionPlan> balanceCluster(TableName tableName, Map<ServerName, List<HRegionInfo>>
123 clusterState) throws HBaseIOException {
124 return balanceCluster(clusterState);
125 }
126
127 @Override
128 public List<RegionPlan> balanceCluster(Map<ServerName, List<HRegionInfo>> clusterState)
129 throws HBaseIOException {
130 if (!isOnline()) {
131 throw new ConstraintException(RSGroupInfoManager.RSGROUP_TABLE_NAME +
132 " is not online, unable to perform balance");
133 }
134
135 Map<ServerName,List<HRegionInfo>> correctedState = correctAssignments(clusterState);
136 List<RegionPlan> regionPlans = new ArrayList<RegionPlan>();
137
138 List<HRegionInfo> misplacedRegions = correctedState.get(LoadBalancer.BOGUS_SERVER_NAME);
139 for (HRegionInfo regionInfo : misplacedRegions) {
140 regionPlans.add(new RegionPlan(regionInfo, null, null));
141 }
142 try {
143
144 HashSet<ServerName> processedServers = new HashSet<>();
145
146
147 for (RSGroupInfo rsgroup : infoManager.listRSGroups()) {
148 Map<ServerName, List<HRegionInfo>> groupClusterState = new HashMap<>();
149 for (ServerName server : clusterState.keySet()) {
150 if (!processedServers.contains(server)
151 && rsgroup.containsServer(server.getAddress())) {
152 List<HRegionInfo> regionsOnServer = correctedState.get(server);
153 groupClusterState.put(server, regionsOnServer);
154 processedServers.add(server);
155 }
156 }
157
158 List<RegionPlan> groupPlans = this.internalBalancer
159 .balanceCluster(groupClusterState);
160 if (groupPlans != null) {
161 regionPlans.addAll(groupPlans);
162 }
163 }
164 } catch (IOException exp) {
165 LOG.warn("Exception while balancing cluster.", exp);
166 regionPlans.clear();
167 }
168 return regionPlans;
169 }
170
171 @Override
172 public Map<ServerName, List<HRegionInfo>> roundRobinAssignment(
173 List<HRegionInfo> regions, List<ServerName> servers) throws HBaseIOException {
174 Map<ServerName, List<HRegionInfo>> assignments = Maps.newHashMap();
175 ListMultimap<String,HRegionInfo> regionMap = ArrayListMultimap.create();
176 ListMultimap<String,ServerName> serverMap = ArrayListMultimap.create();
177 generateGroupMaps(regions, servers, regionMap, serverMap);
178 for(String groupKey : regionMap.keySet()) {
179 if (regionMap.get(groupKey).size() > 0) {
180 Map<ServerName, List<HRegionInfo>> result =
181 this.internalBalancer.roundRobinAssignment(
182 regionMap.get(groupKey),
183 serverMap.get(groupKey));
184 if(result != null) {
185 if(result.containsKey(LoadBalancer.BOGUS_SERVER_NAME) &&
186 assignments.containsKey(LoadBalancer.BOGUS_SERVER_NAME)){
187 assignments.get(LoadBalancer.BOGUS_SERVER_NAME).addAll(
188 result.get(LoadBalancer.BOGUS_SERVER_NAME));
189 } else {
190 assignments.putAll(result);
191 }
192 }
193 }
194 }
195 return assignments;
196 }
197
198 @Override
199 public Map<ServerName, List<HRegionInfo>> retainAssignment(
200 Map<HRegionInfo, ServerName> regions, List<ServerName> servers) throws HBaseIOException {
201 try {
202 Map<ServerName, List<HRegionInfo>> assignments = new TreeMap<ServerName, List<HRegionInfo>>();
203 ListMultimap<String, HRegionInfo> groupToRegion = ArrayListMultimap.create();
204 Set<HRegionInfo> misplacedRegions = getMisplacedRegions(regions);
205 for (HRegionInfo region : regions.keySet()) {
206 if (!misplacedRegions.contains(region)) {
207 String groupName = infoManager.getRSGroupOfTable(region.getTable());
208 if (groupName == null) {
209 LOG.debug("Group not found for table " + region.getTable() + ", using default");
210 groupName = RSGroupInfo.DEFAULT_GROUP;
211 }
212 groupToRegion.put(groupName, region);
213 }
214 }
215
216
217 for (String key : groupToRegion.keySet()) {
218 Map<HRegionInfo, ServerName> currentAssignmentMap = new TreeMap<HRegionInfo, ServerName>();
219 List<HRegionInfo> regionList = groupToRegion.get(key);
220 RSGroupInfo info = infoManager.getRSGroup(key);
221 List<ServerName> candidateList = filterOfflineServers(info, servers);
222 for (HRegionInfo region : regionList) {
223 currentAssignmentMap.put(region, regions.get(region));
224 }
225 if(candidateList.size() > 0) {
226 assignments.putAll(this.internalBalancer.retainAssignment(
227 currentAssignmentMap, candidateList));
228 }
229 }
230
231 for (HRegionInfo region : misplacedRegions) {
232 String groupName = infoManager.getRSGroupOfTable(region.getTable());
233 if (groupName == null) {
234 LOG.debug("Group not found for table " + region.getTable() + ", using default");
235 groupName = RSGroupInfo.DEFAULT_GROUP;
236 }
237 RSGroupInfo info = infoManager.getRSGroup(groupName);
238 List<ServerName> candidateList = filterOfflineServers(info, servers);
239 ServerName server = this.internalBalancer.randomAssignment(region,
240 candidateList);
241 if (server != null) {
242 if (!assignments.containsKey(server)) {
243 assignments.put(server, new ArrayList<HRegionInfo>());
244 }
245 assignments.get(server).add(region);
246 } else {
247
248 if(!assignments.containsKey(LoadBalancer.BOGUS_SERVER_NAME)) {
249 assignments.put(LoadBalancer.BOGUS_SERVER_NAME, new ArrayList<HRegionInfo>());
250 }
251 assignments.get(LoadBalancer.BOGUS_SERVER_NAME).add(region);
252 }
253 }
254 return assignments;
255 } catch (IOException e) {
256 throw new HBaseIOException("Failed to do online retain assignment", e);
257 }
258 }
259
260 @Override
261 public Map<HRegionInfo, ServerName> immediateAssignment(List<HRegionInfo> regions,
262 List<ServerName> servers) throws HBaseIOException {
263 throw new UnsupportedOperationException("immediateAssignment is not supported");
264 }
265
266 @Override
267 public ServerName randomAssignment(HRegionInfo region,
268 List<ServerName> servers) throws HBaseIOException {
269 ListMultimap<String,HRegionInfo> regionMap = LinkedListMultimap.create();
270 ListMultimap<String,ServerName> serverMap = LinkedListMultimap.create();
271 generateGroupMaps(Lists.newArrayList(region), servers, regionMap, serverMap);
272 List<ServerName> filteredServers = serverMap.get(regionMap.keySet().iterator().next());
273 return this.internalBalancer.randomAssignment(region, filteredServers);
274 }
275
276 private void generateGroupMaps(
277 List<HRegionInfo> regions,
278 List<ServerName> servers,
279 ListMultimap<String, HRegionInfo> regionMap,
280 ListMultimap<String, ServerName> serverMap) throws HBaseIOException {
281 try {
282 for (HRegionInfo region : regions) {
283 String groupName = infoManager.getRSGroupOfTable(region.getTable());
284 if (groupName == null) {
285 LOG.debug("Group not found for table " + region.getTable() + ", using default");
286 groupName = RSGroupInfo.DEFAULT_GROUP;
287 }
288 regionMap.put(groupName, region);
289 }
290 for (String groupKey : regionMap.keySet()) {
291 RSGroupInfo info = infoManager.getRSGroup(groupKey);
292 serverMap.putAll(groupKey, filterOfflineServers(info, servers));
293 if(serverMap.get(groupKey).size() < 1) {
294 serverMap.put(groupKey, LoadBalancer.BOGUS_SERVER_NAME);
295 }
296 }
297 } catch(IOException e) {
298 throw new HBaseIOException("Failed to generate group maps", e);
299 }
300 }
301
302 private List<ServerName> filterOfflineServers(RSGroupInfo RSGroupInfo,
303 List<ServerName> onlineServers) {
304 if (RSGroupInfo != null) {
305 return filterServers(RSGroupInfo.getServers(), onlineServers);
306 } else {
307 LOG.debug("Group Information found to be null. Some regions might be unassigned.");
308 return Collections.emptyList();
309 }
310 }
311
312
313
314
315
316
317
318
319
320
321 private List<ServerName> filterServers(Set<Address> servers,
322 List<ServerName> onlineServers) {
323
324
325
326
327
328
329 ArrayList<ServerName> finalList = new ArrayList<>();
330 for (ServerName onlineServer : onlineServers) {
331 if (servers.contains(onlineServer.getAddress())) {
332 finalList.add(onlineServer);
333 }
334 }
335
336 return finalList;
337 }
338
339 public Set<HRegionInfo> getMisplacedRegions(
340 Map<HRegionInfo, ServerName> regions) throws IOException {
341 Set<HRegionInfo> misplacedRegions = new HashSet<HRegionInfo>();
342 for(Map.Entry<HRegionInfo, ServerName> region : regions.entrySet()) {
343 HRegionInfo regionInfo = region.getKey();
344 ServerName assignedServer = region.getValue();
345 String groupName = infoManager.getRSGroupOfTable(regionInfo.getTable());
346 if (groupName == null) {
347 LOG.debug("Group not found for table " + regionInfo.getTable() + ", using default");
348 groupName = RSGroupInfo.DEFAULT_GROUP;
349 }
350 RSGroupInfo info = infoManager.getRSGroup(groupName);
351 if (assignedServer == null) {
352 LOG.debug("There is no assigned server for " + region);
353 continue;
354 }
355 RSGroupInfo otherInfo = infoManager.getRSGroupOfServer(assignedServer.getAddress());
356 if (info == null && otherInfo == null) {
357 LOG.warn("Couldn't obtain rs group information for " + region + " on " + assignedServer);
358 continue;
359 }
360 if ((info == null || !info.containsServer(assignedServer.getAddress()))) {
361 LOG.debug("Found misplaced region: " + regionInfo.getRegionNameAsString() +
362 " on server: " + assignedServer +
363 " found in group: " + otherInfo +
364 " outside of group: " + (info == null ? "UNKNOWN" : info.getName()));
365 misplacedRegions.add(regionInfo);
366 }
367 }
368 return misplacedRegions;
369 }
370
371 private Map<ServerName, List<HRegionInfo>> correctAssignments(
372 Map<ServerName, List<HRegionInfo>> existingAssignments) {
373 Map<ServerName, List<HRegionInfo>> correctAssignments =
374 new TreeMap<ServerName, List<HRegionInfo>>();
375 correctAssignments.put(LoadBalancer.BOGUS_SERVER_NAME, new LinkedList<HRegionInfo>());
376 for (Map.Entry<ServerName, List<HRegionInfo>> assignments : existingAssignments.entrySet()){
377 ServerName sName = assignments.getKey();
378 correctAssignments.put(sName, new LinkedList<HRegionInfo>());
379 List<HRegionInfo> regions = assignments.getValue();
380 for (HRegionInfo region : regions) {
381 RSGroupInfo info = null;
382 try {
383 String groupName = infoManager.getRSGroupOfTable(region.getTable());
384 if (groupName == null) {
385 LOG.debug("Group not found for table " + region.getTable() + ", using default");
386 groupName = RSGroupInfo.DEFAULT_GROUP;
387 }
388 info = infoManager.getRSGroup(groupName);
389 } catch (IOException exp) {
390 LOG.debug("Group information null for region of table " + region.getTable(),
391 exp);
392 }
393 if ((info == null) || (!info.containsServer(sName.getAddress()))) {
394 correctAssignments.get(LoadBalancer.BOGUS_SERVER_NAME).add(region);
395 } else {
396 correctAssignments.get(sName).add(region);
397 }
398 }
399 }
400 return correctAssignments;
401 }
402
403 @Override
404 public void initialize() throws HBaseIOException {
405 try {
406 if (infoManager == null) {
407 List<RSGroupAdminEndpoint> cps =
408 masterServices.getMasterCoprocessorHost().findCoprocessors(RSGroupAdminEndpoint.class);
409 if (cps.size() != 1) {
410 String msg = "Expected one implementation of GroupAdminEndpoint but found " + cps.size();
411 LOG.error(msg);
412 throw new HBaseIOException(msg);
413 }
414 infoManager = cps.get(0).getGroupInfoManager();
415 if(infoManager == null){
416 String msg = "RSGroupInfoManager hasn't been initialized";
417 LOG.error(msg);
418 throw new HBaseIOException(msg);
419 }
420 infoManager.start();
421 }
422 } catch (IOException e) {
423 throw new HBaseIOException("Failed to initialize GroupInfoManagerImpl", e);
424 }
425
426
427 Class<? extends LoadBalancer> balancerKlass = config.getClass(
428 HBASE_GROUP_LOADBALANCER_CLASS,
429 StochasticLoadBalancer.class, LoadBalancer.class);
430 internalBalancer = ReflectionUtils.newInstance(balancerKlass, config);
431 if (clusterStatus != null) {
432 internalBalancer.setClusterStatus(clusterStatus);
433 }
434 internalBalancer.setMasterServices(masterServices);
435 internalBalancer.setConf(config);
436 internalBalancer.initialize();
437 }
438
439 public boolean isOnline() {
440 return infoManager != null && infoManager.isOnline();
441 }
442
443 @Override
444 public void regionOnline(HRegionInfo regionInfo, ServerName sn) {
445 }
446
447 @Override
448 public void regionOffline(HRegionInfo regionInfo) {
449 }
450
451 @Override
452 public void onConfigurationChange(Configuration conf) {
453 internalBalancer.onConfigurationChange(conf);
454 }
455
456 @Override
457 public void stop(String why) {
458 }
459
460 @Override
461 public boolean isStopped() {
462 return false;
463 }
464
465 @Override
466 public void postMasterStartupInitialize() {
467 this.internalBalancer.postMasterStartupInitialize();
468 }
469
470 public void updateBalancerStatus(boolean status) {
471 internalBalancer.updateBalancerStatus(status);
472 }
473 }