1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.hadoop.hbase.master.normalizer;
20
21 import java.util.ArrayList;
22 import java.util.Collections;
23 import java.util.Comparator;
24 import java.util.List;
25
26 import org.apache.commons.logging.Log;
27 import org.apache.commons.logging.LogFactory;
28 import org.apache.hadoop.hbase.HBaseIOException;
29 import org.apache.hadoop.hbase.HRegionInfo;
30 import org.apache.hadoop.hbase.RegionLoad;
31 import org.apache.hadoop.hbase.ServerName;
32 import org.apache.hadoop.hbase.TableName;
33 import org.apache.hadoop.hbase.classification.InterfaceAudience;
34 import org.apache.hadoop.hbase.client.Admin.MasterSwitchType;
35 import org.apache.hadoop.hbase.master.MasterRpcServices;
36 import org.apache.hadoop.hbase.master.MasterServices;
37 import org.apache.hadoop.hbase.protobuf.RequestConverter;
38
39 import com.google.protobuf.ServiceException;
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60 @InterfaceAudience.Private
61 public class SimpleRegionNormalizer implements RegionNormalizer {
62
63 private static final Log LOG = LogFactory.getLog(SimpleRegionNormalizer.class);
64 private static final int MIN_REGION_COUNT = 3;
65 private MasterServices masterServices;
66 private MasterRpcServices masterRpcServices;
67
68
69
70
71
72 @Override
73 public void setMasterServices(MasterServices masterServices) {
74 this.masterServices = masterServices;
75 }
76
77
78 private Comparator<NormalizationPlan> planComparator =
79 new Comparator<NormalizationPlan>() {
80 @Override
81 public int compare(NormalizationPlan plan, NormalizationPlan plan2) {
82 if (plan instanceof SplitNormalizationPlan) {
83 return -1;
84 }
85 if (plan2 instanceof SplitNormalizationPlan) {
86 return 1;
87 }
88 return 0;
89 }
90 };
91
92 @Override
93 public void setMasterRpcServices(MasterRpcServices masterRpcServices) {
94 this.masterRpcServices = masterRpcServices;
95 }
96
97
98
99
100
101
102
103
104 @Override
105 public List<NormalizationPlan> computePlanForTable(TableName table) throws HBaseIOException {
106 if (table == null || table.isSystemTable()) {
107 LOG.debug("Normalization of system table " + table + " isn't allowed");
108 return null;
109 }
110
111 List<NormalizationPlan> plans = new ArrayList<NormalizationPlan>();
112 List<HRegionInfo> tableRegions = masterServices.getAssignmentManager().getRegionStates().
113 getRegionsOfTable(table);
114
115
116 if (tableRegions == null || tableRegions.size() < MIN_REGION_COUNT) {
117 int nrRegions = tableRegions == null ? 0 : tableRegions.size();
118 LOG.debug("Table " + table + " has " + nrRegions + " regions, required min number"
119 + " of regions for normalizer to run is " + MIN_REGION_COUNT + ", not running normalizer");
120 return null;
121 }
122
123 LOG.debug("Computing normalization plan for table: " + table +
124 ", number of regions: " + tableRegions.size());
125
126 long totalSizeMb = 0;
127
128 for (int i = 0; i < tableRegions.size(); i++) {
129 HRegionInfo hri = tableRegions.get(i);
130 long regionSize = getRegionSize(hri);
131 totalSizeMb += regionSize;
132 }
133
134 double avgRegionSize = totalSizeMb / (double) tableRegions.size();
135
136 LOG.debug("Table " + table + ", total aggregated regions size: " + totalSizeMb);
137 LOG.debug("Table " + table + ", average region size: " + avgRegionSize);
138
139 int candidateIdx = 0;
140 boolean splitEnabled = true, mergeEnabled = true;
141 try {
142 splitEnabled = masterRpcServices.isSplitOrMergeEnabled(null,
143 RequestConverter.buildIsSplitOrMergeEnabledRequest(MasterSwitchType.SPLIT)).getEnabled();
144 } catch (ServiceException se) {
145 LOG.debug("Unable to determine whether split is enabled", se);
146 }
147 try {
148 mergeEnabled = masterRpcServices.isSplitOrMergeEnabled(null,
149 RequestConverter.buildIsSplitOrMergeEnabledRequest(MasterSwitchType.MERGE)).getEnabled();
150 } catch (ServiceException se) {
151 LOG.debug("Unable to determine whether split is enabled", se);
152 }
153 while (candidateIdx < tableRegions.size()) {
154 HRegionInfo hri = tableRegions.get(candidateIdx);
155 long regionSize = getRegionSize(hri);
156
157
158 if (regionSize > 2 * avgRegionSize) {
159 if (splitEnabled) {
160 LOG.info("Table " + table + ", large region " + hri.getRegionNameAsString() + " has size "
161 + regionSize + ", more than twice avg size, splitting");
162 plans.add(new SplitNormalizationPlan(hri, null));
163 }
164 } else {
165 if (candidateIdx == tableRegions.size()-1) {
166 break;
167 }
168 if (mergeEnabled) {
169 HRegionInfo hri2 = tableRegions.get(candidateIdx+1);
170 long regionSize2 = getRegionSize(hri2);
171 if (regionSize + regionSize2 < avgRegionSize) {
172 LOG.info("Table " + table + ", small region size: " + regionSize
173 + " plus its neighbor size: " + regionSize2
174 + ", less than the avg size " + avgRegionSize + ", merging them");
175 plans.add(new MergeNormalizationPlan(hri, hri2));
176 candidateIdx++;
177 }
178 }
179 }
180 candidateIdx++;
181 }
182 if (plans.isEmpty()) {
183 LOG.debug("No normalization needed, regions look good for table: " + table);
184 return null;
185 }
186 Collections.sort(plans, planComparator);
187 return plans;
188 }
189
190 private long getRegionSize(HRegionInfo hri) {
191 ServerName sn = masterServices.getAssignmentManager().getRegionStates().
192 getRegionServerOfRegion(hri);
193 RegionLoad regionLoad = masterServices.getServerManager().getLoad(sn).
194 getRegionsLoad().get(hri.getRegionName());
195 return regionLoad.getStorefileSizeMB();
196 }
197 }