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 int acutalRegionCnt = 0;
128
129 for (int i = 0; i < tableRegions.size(); i++) {
130 HRegionInfo hri = tableRegions.get(i);
131 long regionSize = getRegionSize(hri);
132 if (regionSize > 0) {
133 acutalRegionCnt++;
134 totalSizeMb += regionSize;
135 }
136 }
137
138 double avgRegionSize = acutalRegionCnt == 0 ? 0 : totalSizeMb / (double) acutalRegionCnt;
139
140 LOG.debug("Table " + table + ", total aggregated regions size: " + totalSizeMb);
141 LOG.debug("Table " + table + ", average region size: " + avgRegionSize);
142
143 int candidateIdx = 0;
144 boolean splitEnabled = true, mergeEnabled = true;
145 try {
146 splitEnabled = masterRpcServices.isSplitOrMergeEnabled(null,
147 RequestConverter.buildIsSplitOrMergeEnabledRequest(MasterSwitchType.SPLIT)).getEnabled();
148 } catch (ServiceException se) {
149 LOG.debug("Unable to determine whether split is enabled", se);
150 }
151 try {
152 mergeEnabled = masterRpcServices.isSplitOrMergeEnabled(null,
153 RequestConverter.buildIsSplitOrMergeEnabledRequest(MasterSwitchType.MERGE)).getEnabled();
154 } catch (ServiceException se) {
155 LOG.debug("Unable to determine whether split is enabled", se);
156 }
157 while (candidateIdx < tableRegions.size()) {
158 HRegionInfo hri = tableRegions.get(candidateIdx);
159 long regionSize = getRegionSize(hri);
160
161
162 if (regionSize > 2 * avgRegionSize) {
163 if (splitEnabled) {
164 LOG.info("Table " + table + ", large region " + hri.getRegionNameAsString() + " has size "
165 + regionSize + ", more than twice avg size, splitting");
166 plans.add(new SplitNormalizationPlan(hri, null));
167 }
168 } else {
169 if (candidateIdx == tableRegions.size()-1) {
170 break;
171 }
172 if (mergeEnabled) {
173 HRegionInfo hri2 = tableRegions.get(candidateIdx+1);
174 long regionSize2 = getRegionSize(hri2);
175 if (regionSize >= 0 && regionSize2 >= 0 && regionSize + regionSize2 < avgRegionSize) {
176 LOG.info("Table " + table + ", small region size: " + regionSize
177 + " plus its neighbor size: " + regionSize2
178 + ", less than the avg size " + avgRegionSize + ", merging them");
179 plans.add(new MergeNormalizationPlan(hri, hri2));
180 candidateIdx++;
181 }
182 }
183 }
184 candidateIdx++;
185 }
186 if (plans.isEmpty()) {
187 LOG.debug("No normalization needed, regions look good for table: " + table);
188 return null;
189 }
190 Collections.sort(plans, planComparator);
191 return plans;
192 }
193
194 private long getRegionSize(HRegionInfo hri) {
195 ServerName sn = masterServices.getAssignmentManager().getRegionStates().
196 getRegionServerOfRegion(hri);
197 RegionLoad regionLoad = masterServices.getServerManager().getLoad(sn).
198 getRegionsLoad().get(hri.getRegionName());
199 if (regionLoad == null) {
200 LOG.debug(hri.getRegionNameAsString() + " was not found in RegionsLoad");
201 return -1;
202 }
203 return regionLoad.getStorefileSizeMB();
204 }
205 }