Skip to content

Commit 7181d25

Browse files
ndimidukrobertkerek-cldr
authored andcommitted
HBASE-23932 Minor improvements to Region Normalizer (apache#1258)
- consolidate checks made by master on behalf of balancer and normalizer: deciding if the master is in a healthy state for running any actions at all (skipRegionManagementAction). Normalizer now does as balancer did previously. - both balancer and normalizer make one final check on above conditions between calculating an action plan and executing the plan. should make the process more responsive to shutdown requests. - change normalizer to only consider acting on a region when it is in the OPEN state. previously we would normalizer attempt to merge a region that was already in a MERGING_NEW,MERGING,MERGED state. - fix some typos in variable names. Signed-off-by: Josh Elser <elserj@apache.org> Signed-off-by: binlijin <binlijin@gmail.com> Author: Nick Dimiduk Reason: Improvement Ref: CDPD-15964 Change-Id: Ia6dc416f41273e1d46cf298c500fb57aecb9288a (cherry picked from commit 5df8b0d)
1 parent 36a3f0b commit 7181d25

File tree

4 files changed

+218
-129
lines changed

4 files changed

+218
-129
lines changed

hbase-server/src/main/java/org/apache/hadoop/hbase/master/HMaster.java

Lines changed: 55 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@
7878
import org.apache.hadoop.hbase.TableNotDisabledException;
7979
import org.apache.hadoop.hbase.TableNotFoundException;
8080
import org.apache.hadoop.hbase.UnknownRegionException;
81+
import org.apache.hadoop.hbase.client.Admin;
8182
import org.apache.hadoop.hbase.client.ColumnFamilyDescriptor;
8283
import org.apache.hadoop.hbase.client.MasterSwitchType;
8384
import org.apache.hadoop.hbase.client.RegionInfo;
@@ -220,6 +221,7 @@
220221
import org.apache.hbase.thirdparty.com.google.common.collect.ImmutableSet;
221222
import org.apache.hbase.thirdparty.com.google.common.collect.Lists;
222223
import org.apache.hbase.thirdparty.com.google.common.collect.Maps;
224+
import org.apache.hbase.thirdparty.org.apache.commons.collections4.CollectionUtils;
223225

224226
import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil;
225227
import org.apache.hadoop.hbase.shaded.protobuf.generated.AdminProtos.GetRegionInfoResponse.CompactionState;
@@ -364,7 +366,7 @@ public void run() {
364366
volatile boolean serviceStarted = false;
365367

366368
// Maximum time we should run balancer for
367-
private final int maxBlancingTime;
369+
private final int maxBalancingTime;
368370
// Maximum percent of regions in transition when balancing
369371
private final double maxRitPercent;
370372

@@ -524,7 +526,7 @@ public HMaster(final Configuration conf)
524526
// preload table descriptor at startup
525527
this.preLoadTableDescriptors = conf.getBoolean("hbase.master.preload.tabledescriptors", true);
526528

527-
this.maxBlancingTime = getMaxBalancingTime();
529+
this.maxBalancingTime = getMaxBalancingTime();
528530
this.maxRitPercent = conf.getDouble(HConstants.HBASE_MASTER_BALANCER_MAX_RIT_PERCENT,
529531
HConstants.DEFAULT_HBASE_MASTER_BALANCER_MAX_RIT_PERCENT);
530532

@@ -1638,21 +1640,36 @@ public boolean balance() throws IOException {
16381640
return balance(false);
16391641
}
16401642

1641-
public boolean balance(boolean force) throws IOException {
1642-
// if master not initialized, don't run balancer.
1643+
/**
1644+
* Checks master state before initiating action over region topology.
1645+
* @param action the name of the action under consideration, for logging.
1646+
* @return {@code true} when the caller should exit early, {@code false} otherwise.
1647+
*/
1648+
private boolean skipRegionManagementAction(final String action) {
16431649
if (!isInitialized()) {
1644-
LOG.debug("Master has not been initialized, don't run balancer.");
1645-
return false;
1650+
LOG.debug("Master has not been initialized, don't run {}.", action);
1651+
return true;
1652+
}
1653+
if (this.getServerManager().isClusterShutdown()) {
1654+
LOG.info("Cluster is shutting down, don't run {}.", action);
1655+
return true;
16461656
}
1647-
16481657
if (isInMaintenanceMode()) {
1649-
LOG.info("Master is in maintenanceMode mode, don't run balancer.");
1658+
LOG.info("Master is in maintenance mode, don't run {}.", action);
1659+
return true;
1660+
}
1661+
return false;
1662+
}
1663+
1664+
public boolean balance(boolean force) throws IOException {
1665+
if (loadBalancerTracker == null || !loadBalancerTracker.isBalancerOn()) {
1666+
return false;
1667+
}
1668+
if (skipRegionManagementAction("balancer")) {
16501669
return false;
16511670
}
16521671

16531672
synchronized (this.balancer) {
1654-
// If balance not true, don't run balancer.
1655-
if (!this.loadBalancerTracker.isBalancerOn()) return false;
16561673
// Only allow one balance run at at time.
16571674
if (this.assignmentManager.hasRegionsInTransition()) {
16581675
List<RegionStateNode> regionsInTransition = assignmentManager.getRegionsInTransition();
@@ -1701,6 +1718,11 @@ public boolean balance(boolean force) throws IOException {
17011718

17021719
List<RegionPlan> plans = this.balancer.balanceCluster(assignments);
17031720

1721+
if (skipRegionManagementAction("balancer")) {
1722+
// make one last check that the cluster isn't shutting down before proceeding.
1723+
return false;
1724+
}
1725+
17041726
List<RegionPlan> sucRPs = executeRegionPlansWithThrottling(plans);
17051727

17061728
if (this.cpHost != null) {
@@ -1721,10 +1743,10 @@ public List<RegionPlan> executeRegionPlansWithThrottling(List<RegionPlan> plans)
17211743
List<RegionPlan> sucRPs = new ArrayList<>();
17221744
int maxRegionsInTransition = getMaxRegionsInTransition();
17231745
long balanceStartTime = System.currentTimeMillis();
1724-
long cutoffTime = balanceStartTime + this.maxBlancingTime;
1746+
long cutoffTime = balanceStartTime + this.maxBalancingTime;
17251747
int rpCount = 0; // number of RegionPlans balanced so far
17261748
if (plans != null && !plans.isEmpty()) {
1727-
int balanceInterval = this.maxBlancingTime / plans.size();
1749+
int balanceInterval = this.maxBalancingTime / plans.size();
17281750
LOG.info("Balancer plans size is " + plans.size() + ", the balance interval is "
17291751
+ balanceInterval + " ms, and the max number regions in transition is "
17301752
+ maxRegionsInTransition);
@@ -1742,18 +1764,18 @@ public List<RegionPlan> executeRegionPlansWithThrottling(List<RegionPlan> plans)
17421764
//rpCount records balance plans processed, does not care if a plan succeeds
17431765
rpCount++;
17441766

1745-
if (this.maxBlancingTime > 0) {
1767+
if (this.maxBalancingTime > 0) {
17461768
balanceThrottling(balanceStartTime + rpCount * balanceInterval, maxRegionsInTransition,
17471769
cutoffTime);
17481770
}
17491771

17501772
// if performing next balance exceeds cutoff time, exit the loop
1751-
if (this.maxBlancingTime > 0 && rpCount < plans.size()
1773+
if (this.maxBalancingTime > 0 && rpCount < plans.size()
17521774
&& System.currentTimeMillis() > cutoffTime) {
17531775
// TODO: After balance, there should not be a cutoff time (keeping it as
17541776
// a security net for now)
17551777
LOG.debug("No more balancing till next balance run; maxBalanceTime="
1756-
+ this.maxBlancingTime);
1778+
+ this.maxBalancingTime);
17571779
break;
17581780
}
17591781
}
@@ -1775,47 +1797,47 @@ public RegionNormalizer getRegionNormalizer() {
17751797
* is globally disabled)
17761798
*/
17771799
public boolean normalizeRegions() throws IOException {
1778-
if (!isInitialized()) {
1779-
LOG.debug("Master has not been initialized, don't run region normalizer.");
1780-
return false;
1781-
}
1782-
if (this.getServerManager().isClusterShutdown()) {
1783-
LOG.info("Cluster is shutting down, don't run region normalizer.");
1800+
if (regionNormalizerTracker == null || !regionNormalizerTracker.isNormalizerOn()) {
1801+
LOG.debug("Region normalization is disabled, don't run region normalizer.");
17841802
return false;
17851803
}
1786-
if (isInMaintenanceMode()) {
1787-
LOG.info("Master is in maintenance mode, don't run region normalizer.");
1804+
if (skipRegionManagementAction("region normalizer")) {
17881805
return false;
17891806
}
1790-
if (!this.regionNormalizerTracker.isNormalizerOn()) {
1791-
LOG.debug("Region normalization is disabled, don't run region normalizer.");
1807+
if (assignmentManager.hasRegionsInTransition()) {
17921808
return false;
17931809
}
17941810

17951811
synchronized (this.normalizer) {
17961812
// Don't run the normalizer concurrently
1813+
17971814
List<TableName> allEnabledTables = new ArrayList<>(
17981815
this.tableStateManager.getTablesInStates(TableState.State.ENABLED));
17991816

18001817
Collections.shuffle(allEnabledTables);
18011818

18021819
for (TableName table : allEnabledTables) {
1803-
if (isInMaintenanceMode()) {
1804-
LOG.debug("Master is in maintenance mode, stop running region normalizer.");
1805-
return false;
1806-
}
1807-
18081820
TableDescriptor tblDesc = getTableDescriptors().get(table);
18091821
if (table.isSystemTable() || (tblDesc != null &&
18101822
!tblDesc.isNormalizationEnabled())) {
18111823
LOG.trace("Skipping normalization for {}, as it's either system"
18121824
+ " table or doesn't have auto normalization turned on", table);
18131825
continue;
18141826
}
1815-
List<NormalizationPlan> plans = this.normalizer.computePlanForTable(table);
1816-
if (plans != null) {
1827+
1828+
// make one last check that the cluster isn't shutting down before proceeding.
1829+
if (skipRegionManagementAction("region normalizer")) {
1830+
return false;
1831+
}
1832+
1833+
final List<NormalizationPlan> plans = this.normalizer.computePlanForTable(table);
1834+
if (CollectionUtils.isEmpty(plans)) {
1835+
return true;
1836+
}
1837+
1838+
try (final Admin admin = clusterConnection.getAdmin()) {
18171839
for (NormalizationPlan plan : plans) {
1818-
plan.execute(clusterConnection.getAdmin());
1840+
plan.execute(admin);
18191841
if (plan.getType() == PlanType.SPLIT) {
18201842
splitPlanCount++;
18211843
} else if (plan.getType() == PlanType.MERGE) {

hbase-server/src/main/java/org/apache/hadoop/hbase/master/normalizer/AbstractRegionNormalizer.java

Lines changed: 49 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/**
1+
/*
22
*
33
* Licensed to the Apache Software Foundation (ASF) under one
44
* or more contributor license agreements. See the NOTICE file
@@ -21,6 +21,7 @@
2121
import java.io.IOException;
2222
import java.util.ArrayList;
2323
import java.util.List;
24+
import java.util.Objects;
2425
import org.apache.hadoop.hbase.RegionMetrics;
2526
import org.apache.hadoop.hbase.ServerName;
2627
import org.apache.hadoop.hbase.Size;
@@ -30,12 +31,12 @@
3031
import org.apache.hadoop.hbase.client.TableDescriptor;
3132
import org.apache.hadoop.hbase.master.MasterRpcServices;
3233
import org.apache.hadoop.hbase.master.MasterServices;
34+
import org.apache.hadoop.hbase.master.RegionState;
35+
import org.apache.hadoop.hbase.master.assignment.RegionStates;
3336
import org.apache.yetus.audience.InterfaceAudience;
3437
import org.slf4j.Logger;
3538
import org.slf4j.LoggerFactory;
36-
3739
import org.apache.hbase.thirdparty.com.google.protobuf.ServiceException;
38-
3940
import org.apache.hadoop.hbase.shaded.protobuf.RequestConverter;
4041

4142
@InterfaceAudience.Private
@@ -108,12 +109,12 @@ protected boolean isSplitEnabled() {
108109
*/
109110
protected double getAverageRegionSize(List<RegionInfo> tableRegions) {
110111
long totalSizeMb = 0;
111-
int acutalRegionCnt = 0;
112+
int actualRegionCnt = 0;
112113
for (RegionInfo hri : tableRegions) {
113114
long regionSize = getRegionSize(hri);
114115
// don't consider regions that are in bytes for averaging the size.
115116
if (regionSize > 0) {
116-
acutalRegionCnt++;
117+
actualRegionCnt++;
117118
totalSizeMb += regionSize;
118119
}
119120
}
@@ -140,73 +141,92 @@ protected double getAverageRegionSize(List<RegionInfo> tableRegions) {
140141
} else if (targetRegionCount > 0) {
141142
avgRegionSize = totalSizeMb / (double) targetRegionCount;
142143
} else {
143-
avgRegionSize = acutalRegionCnt == 0 ? 0 : totalSizeMb / (double) acutalRegionCnt;
144+
avgRegionSize = actualRegionCnt == 0 ? 0 : totalSizeMb / (double) actualRegionCnt;
144145
}
145146

146147
LOG.debug("Table {}, total aggregated regions size: {} and average region size {}", table,
147148
totalSizeMb, avgRegionSize);
148149
return avgRegionSize;
149150
}
150151

152+
/**
153+
* Determine if a region in {@link RegionState} should be considered for a merge operation.
154+
*/
155+
private static boolean skipForMerge(final RegionState state) {
156+
return state == null || !Objects.equals(state.getState(), RegionState.State.OPEN);
157+
}
158+
151159
/**
152160
* Computes the merge plans that should be executed for this table to converge average region
153161
* towards target average or target region count
154162
* @param table table to normalize
155163
* @return list of merge normalization plans
156164
*/
157165
protected List<NormalizationPlan> getMergeNormalizationPlan(TableName table) {
158-
List<NormalizationPlan> plans = new ArrayList<>();
159-
List<RegionInfo> tableRegions =
160-
masterServices.getAssignmentManager().getRegionStates().getRegionsOfTable(table);
161-
double avgRegionSize = getAverageRegionSize(tableRegions);
162-
LOG.debug("Table {}, average region size: {}.\n Computing normalization plan for table: {}, "
166+
final RegionStates regionStates = masterServices.getAssignmentManager().getRegionStates();
167+
final List<RegionInfo> tableRegions = regionStates.getRegionsOfTable(table);
168+
final double avgRegionSize = getAverageRegionSize(tableRegions);
169+
LOG.debug("Table {}, average region size: {}. Computing normalization plan for table: {}, "
163170
+ "number of regions: {}",
164171
table, avgRegionSize, table, tableRegions.size());
165172

166-
int candidateIdx = 0;
167-
while (candidateIdx < tableRegions.size() - 1) {
168-
RegionInfo hri = tableRegions.get(candidateIdx);
169-
long regionSize = getRegionSize(hri);
170-
RegionInfo hri2 = tableRegions.get(candidateIdx + 1);
171-
long regionSize2 = getRegionSize(hri2);
173+
final List<NormalizationPlan> plans = new ArrayList<>();
174+
for (int candidateIdx = 0; candidateIdx < tableRegions.size() - 1; candidateIdx++) {
175+
final RegionInfo hri = tableRegions.get(candidateIdx);
176+
final RegionInfo hri2 = tableRegions.get(candidateIdx + 1);
177+
if (skipForMerge(regionStates.getRegionState(hri))) {
178+
continue;
179+
}
180+
if (skipForMerge(regionStates.getRegionState(hri2))) {
181+
continue;
182+
}
183+
final long regionSize = getRegionSize(hri);
184+
final long regionSize2 = getRegionSize(hri2);
185+
172186
if (regionSize >= 0 && regionSize2 >= 0 && regionSize + regionSize2 < avgRegionSize) {
173-
// atleast one of the two regions should be older than MIN_REGION_DURATION days
187+
// at least one of the two regions should be older than MIN_REGION_DURATION days
174188
plans.add(new MergeNormalizationPlan(hri, hri2));
175189
candidateIdx++;
176190
} else {
177191
LOG.debug("Skipping region {} of table {} with size {}", hri.getRegionNameAsString(), table,
178192
regionSize);
179193
}
180-
candidateIdx++;
181194
}
182195
return plans;
183196
}
184197

198+
/**
199+
* Determine if a region in {@link RegionState} should be considered for a split operation.
200+
*/
201+
private static boolean skipForSplit(final RegionState state) {
202+
return state == null || !Objects.equals(state.getState(), RegionState.State.OPEN);
203+
}
204+
185205
/**
186206
* Computes the split plans that should be executed for this table to converge average region size
187207
* towards target average or target region count
188208
* @param table table to normalize
189209
* @return list of split normalization plans
190210
*/
191211
protected List<NormalizationPlan> getSplitNormalizationPlan(TableName table) {
192-
List<NormalizationPlan> plans = new ArrayList<>();
193-
List<RegionInfo> tableRegions =
194-
masterServices.getAssignmentManager().getRegionStates().getRegionsOfTable(table);
195-
double avgRegionSize = getAverageRegionSize(tableRegions);
212+
final RegionStates regionStates = masterServices.getAssignmentManager().getRegionStates();
213+
final List<RegionInfo> tableRegions = regionStates.getRegionsOfTable(table);
214+
final double avgRegionSize = getAverageRegionSize(tableRegions);
196215
LOG.debug("Table {}, average region size: {}", table, avgRegionSize);
197216

198-
int candidateIdx = 0;
199-
while (candidateIdx < tableRegions.size()) {
200-
RegionInfo hri = tableRegions.get(candidateIdx);
217+
final List<NormalizationPlan> plans = new ArrayList<>();
218+
for (final RegionInfo hri : tableRegions) {
219+
if (skipForSplit(regionStates.getRegionState(hri))) {
220+
continue;
221+
}
201222
long regionSize = getRegionSize(hri);
202223
// if the region is > 2 times larger than average, we split it, split
203224
// is more high priority normalization action than merge.
204225
if (regionSize > 2 * avgRegionSize) {
205-
LOG.info("Table {}, large region {} has size {}, more than twice avg size, splitting",
206-
table, hri.getRegionNameAsString(), regionSize);
226+
LOG.info("Table {}, large region {} has size {}, more than twice avg size {}, splitting",
227+
table, hri.getRegionNameAsString(), regionSize, avgRegionSize);
207228
plans.add(new SplitNormalizationPlan(hri, null));
208229
}
209-
candidateIdx++;
210230
}
211231
return plans;
212232
}

0 commit comments

Comments
 (0)