Skip to content

Commit 73e0304

Browse files
PierreZwchevreuil
authored andcommitted
HBASE-22618 added the possibility to load custom cost functions
Signed-off-by: Wellington Chevreuil <wchevreuil@apache.org>
1 parent 30ab723 commit 73e0304

File tree

3 files changed

+118
-34
lines changed

3 files changed

+118
-34
lines changed

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

Lines changed: 75 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
import org.apache.hadoop.hbase.master.balancer.BaseLoadBalancer.Cluster.MoveRegionAction;
5353
import org.apache.hadoop.hbase.master.balancer.BaseLoadBalancer.Cluster.SwapRegionsAction;
5454
import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
55+
import org.apache.hadoop.hbase.util.ReflectionUtils;
5556

5657
import com.google.common.base.Optional;
5758

@@ -81,6 +82,13 @@
8182
* <li>hbase.master.balancer.stochastic.storefileSizeCost</li>
8283
* </ul>
8384
*
85+
* <p>You can also add custom Cost function by setting the the following configuration value:</p>
86+
* <ul>
87+
* <li>hbase.master.balancer.stochastic.additionalCostFunctions</li>
88+
* </ul>
89+
*
90+
* <p>All custom Cost Functions needs to extends {@link StochasticLoadBalancer.CostFunction}</p>
91+
*
8492
* <p>In addition to the above configurations, the balancer can be tuned by the following
8593
* configuration values:</p>
8694
* <ul>
@@ -116,6 +124,8 @@ public class StochasticLoadBalancer extends BaseLoadBalancer {
116124
private static final String TABLE_FUNCTION_SEP = "_";
117125
protected static final String MIN_COST_NEED_BALANCE_KEY =
118126
"hbase.master.balancer.stochastic.minCostNeedBalance";
127+
protected static final String COST_FUNCTIONS_COST_FUNCTIONS_KEY =
128+
"hbase.master.balancer.stochastic.additionalCostFunctions";
119129

120130
private static final Random RANDOM = new Random(System.currentTimeMillis());
121131
private static final Log LOG = LogFactory.getLog(StochasticLoadBalancer.class);
@@ -132,7 +142,7 @@ public class StochasticLoadBalancer extends BaseLoadBalancer {
132142

133143
private CandidateGenerator[] candidateGenerators;
134144
private CostFromRegionLoadFunction[] regionLoadFunctions;
135-
private CostFunction[] costFunctions; // FindBugs: Wants this protected; IS2_INCONSISTENT_SYNC
145+
private List<CostFunction> costFunctions; // FindBugs: Wants this protected; IS2_INCONSISTENT_SYNC
136146

137147
// to save and report costs to JMX
138148
private Double curOverallCost = 0d;
@@ -203,24 +213,55 @@ public synchronized void setConf(Configuration conf) {
203213
regionReplicaHostCostFunction = new RegionReplicaHostCostFunction(conf);
204214
regionReplicaRackCostFunction = new RegionReplicaRackCostFunction(conf);
205215

206-
costFunctions = new CostFunction[]{
207-
new RegionCountSkewCostFunction(conf),
208-
new PrimaryRegionCountSkewCostFunction(conf),
209-
new MoveCostFunction(conf),
210-
localityCost,
211-
rackLocalityCost,
212-
new TableSkewCostFunction(conf),
213-
regionReplicaHostCostFunction,
214-
regionReplicaRackCostFunction,
215-
regionLoadFunctions[0],
216-
regionLoadFunctions[1],
217-
regionLoadFunctions[2],
218-
regionLoadFunctions[3],
219-
};
216+
costFunctions = new ArrayList<>();
217+
costFunctions.add(new RegionCountSkewCostFunction(conf));
218+
costFunctions.add(new PrimaryRegionCountSkewCostFunction(conf));
219+
costFunctions.add(new MoveCostFunction(conf));
220+
costFunctions.add(localityCost);
221+
costFunctions.add(rackLocalityCost);
222+
costFunctions.add(new TableSkewCostFunction(conf));
223+
costFunctions.add(regionReplicaHostCostFunction);
224+
costFunctions.add(regionReplicaRackCostFunction);
225+
costFunctions.add(regionLoadFunctions[0]);
226+
costFunctions.add(regionLoadFunctions[1]);
227+
costFunctions.add(regionLoadFunctions[2]);
228+
costFunctions.add(regionLoadFunctions[3]);
229+
loadCustomCostFunctions(conf);
230+
231+
curFunctionCosts = new Double[costFunctions.size()];
232+
tempFunctionCosts = new Double[costFunctions.size()];
233+
234+
LOG.info("Loaded config; maxSteps=" + maxSteps + ", stepsPerRegion=" + stepsPerRegion +
235+
", maxRunningTime=" + maxRunningTime + ", isByTable=" + isByTable + ", etc." +
236+
", maxRunningTime=" + maxRunningTime + ", isByTable=" + isByTable + ", CostFunctions=" +
237+
Arrays.toString(getCostFunctionNames()) + " etc.");
238+
}
239+
240+
private void loadCustomCostFunctions(Configuration conf) {
241+
String[] functionsNames = conf.getStrings(COST_FUNCTIONS_COST_FUNCTIONS_KEY);
220242

221-
curFunctionCosts= new Double[costFunctions.length];
222-
tempFunctionCosts= new Double[costFunctions.length];
243+
if (null == functionsNames) {
244+
return;
245+
}
246+
247+
for(String functionName: functionsNames) {
248+
249+
Class<? extends CostFunction> klass = null;
250+
try {
251+
klass = (Class<? extends CostFunction>) Class.forName(functionName);
252+
if (klass == null) {
253+
continue;
254+
}
255+
CostFunction reflected = ReflectionUtils.newInstance(klass, conf);
223256

257+
LOG.info("Successfully loaded custom CostFunction '" +
258+
reflected.getClass().getSimpleName() + "'");
259+
260+
this.costFunctions.add(reflected);
261+
} catch (ClassNotFoundException e) {
262+
LOG.warn("Cannot load class " + functionName + "': " + e.getMessage());
263+
}
264+
}
224265
}
225266

226267
@Override
@@ -475,8 +516,8 @@ private void updateStochasticCosts(TableName tableName, Double overall, Double[]
475516
"Overall", "Overall cost", overall);
476517

477518
// each cost function
478-
for (int i = 0; i < costFunctions.length; i++) {
479-
CostFunction costFunction = costFunctions[i];
519+
for (int i = 0; i < costFunctions.size(); i++) {
520+
CostFunction costFunction = costFunctions.get(i);
480521
String costFunctionName = costFunction.getClass().getSimpleName();
481522
Double costPercent = (overall == 0) ? 0 : (subCosts[i] / overall);
482523
// TODO: cost function may need a specific description
@@ -579,9 +620,9 @@ protected void updateCostsWithAction(Cluster cluster, Action action) {
579620
*/
580621
public String[] getCostFunctionNames() {
581622
if (costFunctions == null) return null;
582-
String[] ret = new String[costFunctions.length];
583-
for (int i = 0; i < costFunctions.length; i++) {
584-
CostFunction c = costFunctions[i];
623+
String[] ret = new String[costFunctions.size()];
624+
for (int i = 0; i < costFunctions.size(); i++) {
625+
CostFunction c = costFunctions.get(i);
585626
ret[i] = c.getClass().getSimpleName();
586627
}
587628

@@ -600,8 +641,8 @@ public String[] getCostFunctionNames() {
600641
protected double computeCost(Cluster cluster, double previousCost) {
601642
double total = 0;
602643

603-
for (int i = 0; i < costFunctions.length; i++) {
604-
CostFunction c = costFunctions[i];
644+
for (int i = 0; i < costFunctions.size(); i++) {
645+
CostFunction c = costFunctions.get(i);
605646
this.tempFunctionCosts[i] = 0.0;
606647

607648
if (c.getMultiplier() <= 0) {
@@ -984,13 +1025,13 @@ Cluster.Action generate(Cluster cluster) {
9841025
/**
9851026
* Base class of StochasticLoadBalancer's Cost Functions.
9861027
*/
987-
abstract static class CostFunction {
1028+
public abstract static class CostFunction {
9881029

9891030
private float multiplier = 0;
9901031

9911032
protected Cluster cluster;
9921033

993-
CostFunction(Configuration c) {
1034+
public CostFunction(Configuration c) {
9941035
}
9951036

9961037
boolean isNeeded() {
@@ -1039,7 +1080,7 @@ void postAction(Action action) {
10391080
protected void regionMoved(int region, int oldServer, int newServer) {
10401081
}
10411082

1042-
abstract double cost();
1083+
protected abstract double cost();
10431084

10441085
/**
10451086
* Function to compute a scaled cost using {@link DescriptiveStatistics}. It
@@ -1134,7 +1175,7 @@ static class MoveCostFunction extends CostFunction {
11341175
}
11351176

11361177
@Override
1137-
double cost() {
1178+
protected double cost() {
11381179
// Try and size the max number of Moves, but always be prepared to move some.
11391180
int maxMoves = Math.max((int) (cluster.numRegions * maxMovesPercent),
11401181
DEFAULT_MAX_MOVES);
@@ -1169,7 +1210,7 @@ static class RegionCountSkewCostFunction extends CostFunction {
11691210
}
11701211

11711212
@Override
1172-
double cost() {
1213+
protected double cost() {
11731214
if (stats == null || stats.length != cluster.numServers) {
11741215
stats = new double[cluster.numServers];
11751216
}
@@ -1201,7 +1242,7 @@ static class PrimaryRegionCountSkewCostFunction extends CostFunction {
12011242
}
12021243

12031244
@Override
1204-
double cost() {
1245+
protected double cost() {
12051246
if (!cluster.hasRegionReplicas) {
12061247
return 0;
12071248
}
@@ -1238,7 +1279,7 @@ static class TableSkewCostFunction extends CostFunction {
12381279
}
12391280

12401281
@Override
1241-
double cost() {
1282+
protected double cost() {
12421283
double max = cluster.numRegions;
12431284
double min = ((double) cluster.numRegions) / cluster.numServers;
12441285
double value = 0;
@@ -1321,7 +1362,7 @@ protected void regionMoved(int region, int oldServer, int newServer) {
13211362
}
13221363

13231364
@Override
1324-
double cost() {
1365+
protected double cost() {
13251366
return 1 - locality;
13261367
}
13271368

@@ -1399,7 +1440,7 @@ void setLoads(Map<String, Deque<RegionLoad>> l) {
13991440
}
14001441

14011442
@Override
1402-
double cost() {
1443+
protected double cost() {
14031444
if (clusterStatus == null || loads == null) {
14041445
return 0;
14051446
}
@@ -1572,7 +1613,7 @@ boolean isNeeded() {
15721613
}
15731614

15741615
@Override
1575-
double cost() {
1616+
protected double cost() {
15761617
if (maxCost <= 0) {
15771618
return 0;
15781619
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/**
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
package org.apache.hadoop.hbase.master.balancer;
19+
20+
import org.apache.hadoop.conf.Configuration;
21+
22+
public class DummyCostFunction extends StochasticLoadBalancer.CostFunction {
23+
public DummyCostFunction(Configuration c) {
24+
super(c);
25+
}
26+
27+
@Override
28+
protected double cost() {
29+
return 0;
30+
}
31+
}

hbase-server/src/test/java/org/apache/hadoop/hbase/master/balancer/TestStochasticLoadBalancer.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -669,6 +669,18 @@ public void testRegionReplicationOnMidClusterWithRacks() {
669669
testWithCluster(serverMap, rm, false, true);
670670
}
671671

672+
@Test
673+
public void testAdditionalCostFunction() {
674+
conf.set(StochasticLoadBalancer.COST_FUNCTIONS_COST_FUNCTIONS_KEY,
675+
DummyCostFunction.class.getName());
676+
677+
loadBalancer.setConf(conf);
678+
System.out.println(Arrays.toString(loadBalancer.getCostFunctionNames()));
679+
assertTrue(Arrays.
680+
asList(loadBalancer.getCostFunctionNames()).
681+
contains(DummyCostFunction.class.getSimpleName()));
682+
}
683+
672684
// This mock allows us to test the LocalityCostFunction
673685
private class MockCluster extends BaseLoadBalancer.Cluster {
674686

0 commit comments

Comments
 (0)