Skip to content

Commit

Permalink
HBASE-25819 Fix style issues for StochasticLoadBalancer (#3207)
Browse files Browse the repository at this point in the history
Signed-off-by: Yulin Niu <[email protected]>
  • Loading branch information
Apache9 authored Apr 29, 2021
1 parent b061b0c commit 6c65314
Show file tree
Hide file tree
Showing 24 changed files with 1,279 additions and 919 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ public static <T> T instantiateWithCustomCtor(String className,
}
}

private static <T> T instantiate(final String className, Constructor<T> ctor, Object[] ctorArgs) {
public static <T> T instantiate(final String className, Constructor<T> ctor, Object... ctorArgs) {
try {
ctor.setAccessible(true);
return ctor.newInstance(ctorArgs);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hadoop.hbase.master.balancer;

import org.apache.hadoop.conf.Configuration;
import org.apache.yetus.audience.InterfaceAudience;

/**
* Compute the cost of total number of coprocessor requests The more unbalanced the higher the
* computed cost will be. This uses a rolling average of regionload.
*/
@InterfaceAudience.Private
class CPRequestCostFunction extends CostFromRegionLoadAsRateFunction {

private static final String CP_REQUEST_COST_KEY =
"hbase.master.balancer.stochastic.cpRequestCost";
private static final float DEFAULT_CP_REQUEST_COST = 5;

CPRequestCostFunction(Configuration conf) {
this.setMultiplier(conf.getFloat(CP_REQUEST_COST_KEY, DEFAULT_CP_REQUEST_COST));
}

@Override
protected double getCostFromRl(BalancerRegionLoad rl) {
return rl.getCpRequestsCount();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hadoop.hbase.master.balancer;

import java.util.Collection;
import org.apache.yetus.audience.InterfaceAudience;

/**
* Class to be used for the subset of RegionLoad costs that should be treated as rates. We do not
* compare about the actual rate in requests per second but rather the rate relative to the rest of
* the regions.
*/
@InterfaceAudience.Private
abstract class CostFromRegionLoadAsRateFunction extends CostFromRegionLoadFunction {

@Override
protected double getRegionLoadCost(Collection<BalancerRegionLoad> regionLoadList) {
double cost = 0;
double previous = 0;
boolean isFirst = true;
for (BalancerRegionLoad rl : regionLoadList) {
double current = getCostFromRl(rl);
if (isFirst) {
isFirst = false;
} else {
cost += current - previous;
}
previous = current;
}
return Math.max(0, cost / (regionLoadList.size() - 1));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hadoop.hbase.master.balancer;

import java.util.Collection;
import java.util.Deque;
import java.util.Map;
import org.apache.hadoop.hbase.ClusterMetrics;
import org.apache.yetus.audience.InterfaceAudience;

/**
* Base class the allows writing costs functions from rolling average of some number from
* RegionLoad.
*/
@InterfaceAudience.Private
abstract class CostFromRegionLoadFunction extends CostFunction {

private ClusterMetrics clusterStatus;
private Map<String, Deque<BalancerRegionLoad>> loads;
private double[] stats;

void setClusterMetrics(ClusterMetrics status) {
this.clusterStatus = status;
}

void setLoads(Map<String, Deque<BalancerRegionLoad>> l) {
this.loads = l;
}

@Override
protected final double cost() {
if (clusterStatus == null || loads == null) {
return 0;
}

if (stats == null || stats.length != cluster.numServers) {
stats = new double[cluster.numServers];
}

for (int i = 0; i < stats.length; i++) {
// Cost this server has from RegionLoad
long cost = 0;

// for every region on this server get the rl
for (int regionIndex : cluster.regionsPerServer[i]) {
Collection<BalancerRegionLoad> regionLoadList = cluster.regionLoads[regionIndex];

// Now if we found a region load get the type of cost that was requested.
if (regionLoadList != null) {
cost = (long) (cost + getRegionLoadCost(regionLoadList));
}
}

// Add the total cost to the stats.
stats[i] = cost;
}

// Now return the scaled cost from data held in the stats object.
return costFromArray(stats);
}

protected double getRegionLoadCost(Collection<BalancerRegionLoad> regionLoadList) {
double cost = 0;
for (BalancerRegionLoad rl : regionLoadList) {
cost += getCostFromRl(rl);
}
return cost / regionLoadList.size();
}

protected abstract double getCostFromRl(BalancerRegionLoad rl);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hadoop.hbase.master.balancer;

import org.apache.yetus.audience.InterfaceAudience;

/**
* Base class of StochasticLoadBalancer's Cost Functions.
*/
@InterfaceAudience.Private
abstract class CostFunction {

private float multiplier = 0;

protected BalancerClusterState cluster;

boolean isNeeded() {
return true;
}

float getMultiplier() {
return multiplier;
}

void setMultiplier(float m) {
this.multiplier = m;
}

/**
* Called once per LB invocation to give the cost function to initialize it's state, and perform
* any costly calculation.
*/
void init(BalancerClusterState cluster) {
this.cluster = cluster;
}

/**
* Called once per cluster Action to give the cost function an opportunity to update it's state.
* postAction() is always called at least once before cost() is called with the cluster that this
* action is performed on.
*/
void postAction(BalanceAction action) {
switch (action.getType()) {
case NULL:
break;
case ASSIGN_REGION:
AssignRegionAction ar = (AssignRegionAction) action;
regionMoved(ar.getRegion(), -1, ar.getServer());
break;
case MOVE_REGION:
MoveRegionAction mra = (MoveRegionAction) action;
regionMoved(mra.getRegion(), mra.getFromServer(), mra.getToServer());
break;
case SWAP_REGIONS:
SwapRegionsAction a = (SwapRegionsAction) action;
regionMoved(a.getFromRegion(), a.getFromServer(), a.getToServer());
regionMoved(a.getToRegion(), a.getToServer(), a.getFromServer());
break;
default:
throw new RuntimeException("Uknown action:" + action.getType());
}
}

protected void regionMoved(int region, int oldServer, int newServer) {
}

protected abstract double cost();

@SuppressWarnings("checkstyle:linelength")
/**
* Function to compute a scaled cost using
* {@link org.apache.commons.math3.stat.descriptive.DescriptiveStatistics#DescriptiveStatistics()}.
* It assumes that this is a zero sum set of costs. It assumes that the worst case possible is all
* of the elements in one region server and the rest having 0.
* @param stats the costs
* @return a scaled set of costs.
*/
protected final double costFromArray(double[] stats) {
double totalCost = 0;
double total = getSum(stats);

double count = stats.length;
double mean = total / count;

// Compute max as if all region servers had 0 and one had the sum of all costs. This must be
// a zero sum cost for this to make sense.
double max = ((count - 1) * mean) + (total - mean);

// It's possible that there aren't enough regions to go around
double min;
if (count > total) {
min = ((count - total) * mean) + ((1 - mean) * total);
} else {
// Some will have 1 more than everything else.
int numHigh = (int) (total - (Math.floor(mean) * count));
int numLow = (int) (count - numHigh);

min = (numHigh * (Math.ceil(mean) - mean)) + (numLow * (mean - Math.floor(mean)));

}
min = Math.max(0, min);
for (int i = 0; i < stats.length; i++) {
double n = stats[i];
double diff = Math.abs(mean - n);
totalCost += diff;
}

double scaled = scale(min, max, totalCost);
return scaled;
}

private double getSum(double[] stats) {
double total = 0;
for (double s : stats) {
total += s;
}
return total;
}

/**
* Scale the value between 0 and 1.
* @param min Min value
* @param max The Max value
* @param value The value to be scaled.
* @return The scaled value.
*/
protected final double scale(double min, double max, double value) {
if (max <= min || value <= min) {
return 0;
}
if ((max - min) == 0) {
return 0;
}

return Math.max(0d, Math.min(1d, (value - min) / (max - min)));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@
* The rule file can be located on local FS or HDFS, depending on the prefix (file//: or hdfs://).
*/
@InterfaceAudience.Private
public class HeterogeneousRegionCountCostFunction extends StochasticLoadBalancer.CostFunction {
public class HeterogeneousRegionCountCostFunction extends CostFunction {

/**
* configuration used for the path where the rule file is stored.
Expand Down Expand Up @@ -94,7 +94,6 @@ public class HeterogeneousRegionCountCostFunction extends StochasticLoadBalancer
double overallUsage;

HeterogeneousRegionCountCostFunction(final Configuration conf) {
super(conf);
this.conf = conf;
this.limitPerRS = new HashMap<>();
this.limitPerRule = new HashMap<>();
Expand All @@ -108,8 +107,8 @@ public class HeterogeneousRegionCountCostFunction extends StochasticLoadBalancer
+ "'. Setting default to 200");
this.defaultNumberOfRegions = 200;
}
if (conf.getFloat(StochasticLoadBalancer.RegionCountSkewCostFunction.REGION_COUNT_SKEW_COST_KEY,
StochasticLoadBalancer.RegionCountSkewCostFunction.DEFAULT_REGION_COUNT_SKEW_COST) > 0) {
if (conf.getFloat(RegionCountSkewCostFunction.REGION_COUNT_SKEW_COST_KEY,
RegionCountSkewCostFunction.DEFAULT_REGION_COUNT_SKEW_COST) > 0) {
LOG.warn("regionCountCost is not set to 0, "
+ " this will interfere with the HeterogeneousRegionCountCostFunction!");
}
Expand Down
Loading

0 comments on commit 6c65314

Please sign in to comment.