5252import org .apache .hadoop .hbase .master .balancer .BaseLoadBalancer .Cluster .MoveRegionAction ;
5353import org .apache .hadoop .hbase .master .balancer .BaseLoadBalancer .Cluster .SwapRegionsAction ;
5454import org .apache .hadoop .hbase .util .EnvironmentEdgeManager ;
55+ import org .apache .hadoop .hbase .util .ReflectionUtils ;
5556
5657import com .google .common .base .Optional ;
5758
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 }
0 commit comments