Skip to content

Commit 45622ab

Browse files
committed
HBASE-24140 : Move CandidateGenerator and their implementors out of StochasticLoadBalancer (#1458)
Signed-off-by: Jan Hentschel <jan.hentschel@ultratendency.com>
1 parent 8a117c9 commit 45622ab

File tree

6 files changed

+413
-315
lines changed

6 files changed

+413
-315
lines changed
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
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+
19+
package org.apache.hadoop.hbase.master.balancer;
20+
21+
import java.util.ArrayList;
22+
import java.util.Collections;
23+
import java.util.List;
24+
import java.util.Map;
25+
26+
import org.apache.hadoop.hbase.client.RegionInfo;
27+
28+
import org.apache.yetus.audience.InterfaceAudience;
29+
30+
/**
31+
* Generates a candidate action to be applied to the cluster for cost function search
32+
*/
33+
@InterfaceAudience.Private
34+
abstract class CandidateGenerator {
35+
36+
abstract BaseLoadBalancer.Cluster.Action generate(BaseLoadBalancer.Cluster cluster);
37+
38+
/**
39+
* From a list of regions pick a random one. Null can be returned which
40+
* {@link StochasticLoadBalancer#balanceCluster(Map)} recognize as signal to try a region move
41+
* rather than swap.
42+
*
43+
* @param cluster The state of the cluster
44+
* @param server index of the server
45+
* @param chanceOfNoSwap Chance that this will decide to try a move rather
46+
* than a swap.
47+
* @return a random {@link RegionInfo} or null if an asymmetrical move is
48+
* suggested.
49+
*/
50+
int pickRandomRegion(BaseLoadBalancer.Cluster cluster, int server,
51+
double chanceOfNoSwap) {
52+
// Check to see if this is just a move.
53+
if (cluster.regionsPerServer[server].length == 0
54+
|| StochasticLoadBalancer.RANDOM.nextFloat() < chanceOfNoSwap) {
55+
// signal a move only.
56+
return -1;
57+
}
58+
int rand = StochasticLoadBalancer.RANDOM.nextInt(cluster.regionsPerServer[server].length);
59+
return cluster.regionsPerServer[server][rand];
60+
}
61+
62+
int pickRandomServer(BaseLoadBalancer.Cluster cluster) {
63+
if (cluster.numServers < 1) {
64+
return -1;
65+
}
66+
67+
return StochasticLoadBalancer.RANDOM.nextInt(cluster.numServers);
68+
}
69+
70+
int pickRandomRack(BaseLoadBalancer.Cluster cluster) {
71+
if (cluster.numRacks < 1) {
72+
return -1;
73+
}
74+
75+
return StochasticLoadBalancer.RANDOM.nextInt(cluster.numRacks);
76+
}
77+
78+
int pickOtherRandomServer(BaseLoadBalancer.Cluster cluster, int serverIndex) {
79+
if (cluster.numServers < 2) {
80+
return -1;
81+
}
82+
while (true) {
83+
int otherServerIndex = pickRandomServer(cluster);
84+
if (otherServerIndex != serverIndex) {
85+
return otherServerIndex;
86+
}
87+
}
88+
}
89+
90+
int pickOtherRandomRack(BaseLoadBalancer.Cluster cluster, int rackIndex) {
91+
if (cluster.numRacks < 2) {
92+
return -1;
93+
}
94+
while (true) {
95+
int otherRackIndex = pickRandomRack(cluster);
96+
if (otherRackIndex != rackIndex) {
97+
return otherRackIndex;
98+
}
99+
}
100+
}
101+
102+
BaseLoadBalancer.Cluster.Action pickRandomRegions(BaseLoadBalancer.Cluster cluster,
103+
int thisServer, int otherServer) {
104+
if (thisServer < 0 || otherServer < 0) {
105+
return BaseLoadBalancer.Cluster.NullAction;
106+
}
107+
108+
// Decide who is most likely to need another region
109+
int thisRegionCount = cluster.getNumRegions(thisServer);
110+
int otherRegionCount = cluster.getNumRegions(otherServer);
111+
112+
// Assign the chance based upon the above
113+
double thisChance = (thisRegionCount > otherRegionCount) ? 0 : 0.5;
114+
double otherChance = (thisRegionCount <= otherRegionCount) ? 0 : 0.5;
115+
116+
int thisRegion = pickRandomRegion(cluster, thisServer, thisChance);
117+
int otherRegion = pickRandomRegion(cluster, otherServer, otherChance);
118+
119+
return getAction(thisServer, thisRegion, otherServer, otherRegion);
120+
}
121+
122+
protected BaseLoadBalancer.Cluster.Action getAction(int fromServer, int fromRegion,
123+
int toServer, int toRegion) {
124+
if (fromServer < 0 || toServer < 0) {
125+
return BaseLoadBalancer.Cluster.NullAction;
126+
}
127+
if (fromRegion > 0 && toRegion > 0) {
128+
return new BaseLoadBalancer.Cluster.SwapRegionsAction(fromServer, fromRegion,
129+
toServer, toRegion);
130+
} else if (fromRegion > 0) {
131+
return new BaseLoadBalancer.Cluster.MoveRegionAction(fromRegion, fromServer, toServer);
132+
} else if (toRegion > 0) {
133+
return new BaseLoadBalancer.Cluster.MoveRegionAction(toRegion, toServer, fromServer);
134+
} else {
135+
return BaseLoadBalancer.Cluster.NullAction;
136+
}
137+
}
138+
139+
/**
140+
* Returns a random iteration order of indexes of an array with size length
141+
*/
142+
List<Integer> getRandomIterationOrder(int length) {
143+
ArrayList<Integer> order = new ArrayList<>(length);
144+
for (int i = 0; i < length; i++) {
145+
order.add(i);
146+
}
147+
Collections.shuffle(order);
148+
return order;
149+
}
150+
151+
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@
6565
* RegionServer as the new Primary RegionServer) after a region is recovered. This
6666
* should help provide consistent read latencies for the regions even when their
6767
* primary region servers die. This provides two
68-
* {@link org.apache.hadoop.hbase.master.balancer.StochasticLoadBalancer.CandidateGenerator}
68+
* {@link CandidateGenerator}
6969
*
7070
*/
7171
@InterfaceAudience.Private
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
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+
19+
package org.apache.hadoop.hbase.master.balancer;
20+
21+
import org.apache.yetus.audience.InterfaceAudience;
22+
23+
@InterfaceAudience.Private
24+
class LoadCandidateGenerator extends CandidateGenerator {
25+
26+
@Override
27+
BaseLoadBalancer.Cluster.Action generate(BaseLoadBalancer.Cluster cluster) {
28+
cluster.sortServersByRegionCount();
29+
int thisServer = pickMostLoadedServer(cluster, -1);
30+
int otherServer = pickLeastLoadedServer(cluster, thisServer);
31+
return pickRandomRegions(cluster, thisServer, otherServer);
32+
}
33+
34+
private int pickLeastLoadedServer(final BaseLoadBalancer.Cluster cluster, int thisServer) {
35+
Integer[] servers = cluster.serverIndicesSortedByRegionCount;
36+
37+
int index = 0;
38+
while (servers[index] == null || servers[index] == thisServer) {
39+
index++;
40+
if (index == servers.length) {
41+
return -1;
42+
}
43+
}
44+
return servers[index];
45+
}
46+
47+
private int pickMostLoadedServer(final BaseLoadBalancer.Cluster cluster, int thisServer) {
48+
Integer[] servers = cluster.serverIndicesSortedByRegionCount;
49+
50+
int index = servers.length - 1;
51+
while (servers[index] == null || servers[index] == thisServer) {
52+
index--;
53+
if (index < 0) {
54+
return -1;
55+
}
56+
}
57+
return servers[index];
58+
}
59+
60+
}
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
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+
19+
package org.apache.hadoop.hbase.master.balancer;
20+
21+
import org.apache.hadoop.hbase.master.MasterServices;
22+
23+
import org.apache.hbase.thirdparty.com.google.common.base.Optional;
24+
25+
import org.apache.yetus.audience.InterfaceAudience;
26+
27+
@InterfaceAudience.Private
28+
class LocalityBasedCandidateGenerator extends CandidateGenerator {
29+
30+
private MasterServices masterServices;
31+
32+
LocalityBasedCandidateGenerator(MasterServices masterServices) {
33+
this.masterServices = masterServices;
34+
}
35+
36+
@Override
37+
BaseLoadBalancer.Cluster.Action generate(BaseLoadBalancer.Cluster cluster) {
38+
if (this.masterServices == null) {
39+
int thisServer = pickRandomServer(cluster);
40+
// Pick the other server
41+
int otherServer = pickOtherRandomServer(cluster, thisServer);
42+
return pickRandomRegions(cluster, thisServer, otherServer);
43+
}
44+
45+
// Randomly iterate through regions until you find one that is not on ideal host
46+
for (int region : getRandomIterationOrder(cluster.numRegions)) {
47+
int currentServer = cluster.regionIndexToServerIndex[region];
48+
if (currentServer != cluster.getOrComputeRegionsToMostLocalEntities(
49+
BaseLoadBalancer.Cluster.LocalityType.SERVER)[region]) {
50+
Optional<BaseLoadBalancer.Cluster.Action> potential = tryMoveOrSwap(cluster,
51+
currentServer, region,
52+
cluster.getOrComputeRegionsToMostLocalEntities(
53+
BaseLoadBalancer.Cluster.LocalityType.SERVER)[region]
54+
);
55+
if (potential.isPresent()) {
56+
return potential.get();
57+
}
58+
}
59+
}
60+
return BaseLoadBalancer.Cluster.NullAction;
61+
}
62+
63+
private Optional<BaseLoadBalancer.Cluster.Action> tryMoveOrSwap(BaseLoadBalancer.Cluster cluster,
64+
int fromServer, int fromRegion, int toServer) {
65+
// Try move first. We know apriori fromRegion has the highest locality on toServer
66+
if (cluster.serverHasTooFewRegions(toServer)) {
67+
return Optional.of(getAction(fromServer, fromRegion, toServer, -1));
68+
}
69+
// Compare locality gain/loss from swapping fromRegion with regions on toServer
70+
double fromRegionLocalityDelta = getWeightedLocality(cluster, fromRegion, toServer)
71+
- getWeightedLocality(cluster, fromRegion, fromServer);
72+
for (int toRegionIndex : getRandomIterationOrder(cluster.regionsPerServer[toServer].length)) {
73+
int toRegion = cluster.regionsPerServer[toServer][toRegionIndex];
74+
double toRegionLocalityDelta = getWeightedLocality(cluster, toRegion, fromServer)
75+
- getWeightedLocality(cluster, toRegion, toServer);
76+
// If locality would remain neutral or improve, attempt the swap
77+
if (fromRegionLocalityDelta + toRegionLocalityDelta >= 0) {
78+
return Optional.of(getAction(fromServer, fromRegion, toServer, toRegion));
79+
}
80+
}
81+
return Optional.absent();
82+
}
83+
84+
private double getWeightedLocality(BaseLoadBalancer.Cluster cluster, int region, int server) {
85+
return cluster.getOrComputeWeightedLocality(region, server,
86+
BaseLoadBalancer.Cluster.LocalityType.SERVER);
87+
}
88+
89+
void setServices(MasterServices services) {
90+
this.masterServices = services;
91+
}
92+
93+
}

0 commit comments

Comments
 (0)