Skip to content

Commit df6e5dd

Browse files
sunhellysaintstack
authored andcommitted
HBASE-25518 Support separate child regions to different region servers (#3001)
Signed-off-by: stack <stack@apache.org>
1 parent 90a58fe commit df6e5dd

File tree

7 files changed

+313
-66
lines changed

7 files changed

+313
-66
lines changed

hbase-common/src/main/java/org/apache/hadoop/hbase/HConstants.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,21 @@ public enum OperationStatusCode {
157157
/** Default value for the balancer period */
158158
public static final int DEFAULT_HBASE_BALANCER_PERIOD = 300000;
159159

160+
/**
161+
* Config key for enable/disable automatically separate child regions to different region servers
162+
* in the procedure of split regions. One child will be kept to the server where parent
163+
* region is on, and the other child will be assigned to a random server.
164+
* See HBASE-25518.
165+
*/
166+
public static final String HBASE_ENABLE_SEPARATE_CHILD_REGIONS =
167+
"hbase.master.auto.separate.child.regions.after.split.enabled";
168+
169+
/**
170+
* Default value for automatically separate child regions to different region servers
171+
* (set to "false" to keep all child regions to the server where parent region is on)
172+
*/
173+
public static final boolean DEFAULT_HBASE_ENABLE_SEPARATE_CHILD_REGIONS = false;
174+
160175
/** The name of the ensemble table */
161176
public static final TableName ENSEMBLE_TABLE_NAME = TableName.valueOf("hbase:ensemble");
162177

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

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import java.util.stream.Stream;
2828
import org.apache.commons.lang3.ArrayUtils;
2929
import org.apache.hadoop.hbase.HBaseIOException;
30+
import org.apache.hadoop.hbase.HConstants;
3031
import org.apache.hadoop.hbase.ServerName;
3132
import org.apache.hadoop.hbase.client.RegionInfo;
3233
import org.apache.hadoop.hbase.client.RegionReplicaUtil;
@@ -43,6 +44,7 @@
4344
import org.apache.hadoop.hbase.shaded.protobuf.generated.AdminProtos.AdminService;
4445
import org.apache.hadoop.hbase.shaded.protobuf.generated.AdminProtos.GetRegionInfoRequest;
4546
import org.apache.hadoop.hbase.shaded.protobuf.generated.AdminProtos.GetRegionInfoResponse;
47+
import static org.apache.hadoop.hbase.HConstants.DEFAULT_HBASE_ENABLE_SEPARATE_CHILD_REGIONS;
4648

4749
/**
4850
* Utility for this assignment package only.
@@ -199,6 +201,67 @@ private static TransitRegionStateProcedure[] createAssignProcedures(MasterProced
199201
return ArrayUtils.addAll(primaryRegionProcs, replicaRegionAssignProcs);
200202
}
201203

204+
/**
205+
* Create round robin assign procedures for the given regions,
206+
* according to the {@code regionReplication}.
207+
* <p/>
208+
* For rolling back, we will submit procedures directly to the {@code ProcedureExecutor}, so it is
209+
* possible that we persist the newly scheduled procedures, and then crash before persisting the
210+
* rollback state, so when we arrive here the second time, it is possible that some regions have
211+
* already been associated with a TRSP.
212+
* @param ignoreIfInTransition if true, will skip creating TRSP for the given region if it is
213+
* already in transition, otherwise we will add an assert that it should not in
214+
* transition.
215+
*/
216+
private static TransitRegionStateProcedure[] createRoundRobinAssignProcedures(
217+
MasterProcedureEnv env, List<RegionInfo> regions, int regionReplication,
218+
List<ServerName> serversToExclude, boolean ignoreIfInTransition) {
219+
List<RegionInfo> regionsAndReplicas = new ArrayList<>(regions);
220+
if (regionReplication != DEFAULT_REGION_REPLICA) {
221+
222+
// collect the replica region infos
223+
List<RegionInfo> replicaRegionInfos =
224+
new ArrayList<RegionInfo>(regions.size() * (regionReplication - 1));
225+
for (RegionInfo hri : regions) {
226+
// start the index from 1
227+
for (int i = 1; i < regionReplication; i++) {
228+
replicaRegionInfos.add(RegionReplicaUtil.getRegionInfoForReplica(hri, i));
229+
}
230+
}
231+
regionsAndReplicas.addAll(replicaRegionInfos);
232+
}
233+
if (ignoreIfInTransition) {
234+
for (RegionInfo region : regionsAndReplicas) {
235+
if (env.getAssignmentManager().getRegionStates().getOrCreateRegionStateNode(region)
236+
.isInTransition()) {
237+
return null;
238+
}
239+
}
240+
}
241+
// create round robin procs. Note that we exclude the primary region's target server
242+
return env.getAssignmentManager()
243+
.createRoundRobinAssignProcedures(regionsAndReplicas, serversToExclude);
244+
}
245+
246+
static TransitRegionStateProcedure[] createAssignProceduresForSplitDaughters(
247+
MasterProcedureEnv env, List<RegionInfo> daughters, int regionReplication,
248+
ServerName parentServer) {
249+
if(env.getMasterConfiguration().getBoolean(HConstants.HBASE_ENABLE_SEPARATE_CHILD_REGIONS,
250+
DEFAULT_HBASE_ENABLE_SEPARATE_CHILD_REGIONS)){
251+
// keep one daughter on the parent region server
252+
TransitRegionStateProcedure[] daughterOne =
253+
createAssignProcedures(env, Collections.singletonList(daughters.get(0)),
254+
regionReplication, parentServer, false);
255+
// round robin assign the other daughter
256+
TransitRegionStateProcedure[] daughterTwo =
257+
createRoundRobinAssignProcedures(env, Collections.singletonList(daughters.get(1)),
258+
regionReplication, Collections.singletonList(parentServer), false);
259+
return ArrayUtils.addAll(daughterOne, daughterTwo);
260+
}
261+
return createAssignProceduresForOpeningNewRegions(env, daughters, regionReplication,
262+
parentServer);
263+
}
264+
202265
static TransitRegionStateProcedure[] createAssignProceduresForOpeningNewRegions(
203266
MasterProcedureEnv env, List<RegionInfo> regions, int regionReplication,
204267
ServerName targetServer) {

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -869,7 +869,7 @@ private TransitRegionStateProcedure[] createAssignProcedures(MasterProcedureEnv
869869
List<RegionInfo> hris = new ArrayList<RegionInfo>(2);
870870
hris.add(daughterOneRI);
871871
hris.add(daughterTwoRI);
872-
return AssignmentManagerUtil.createAssignProceduresForOpeningNewRegions(env, hris,
872+
return AssignmentManagerUtil.createAssignProceduresForSplitDaughters(env, hris,
873873
getRegionReplication(env), getParentRegionServerName(env));
874874
}
875875

hbase-server/src/test/java/org/apache/hadoop/hbase/master/assignment/AssignmentTestingUtil.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,16 @@
2424
import java.util.Set;
2525
import org.apache.hadoop.hbase.HBaseTestingUtility;
2626
import org.apache.hadoop.hbase.ServerName;
27+
import org.apache.hadoop.hbase.TableName;
2728
import org.apache.hadoop.hbase.Waiter;
2829
import org.apache.hadoop.hbase.Waiter.ExplainingPredicate;
30+
import org.apache.hadoop.hbase.client.Put;
2931
import org.apache.hadoop.hbase.client.RegionInfo;
32+
import org.apache.hadoop.hbase.client.Table;
3033
import org.apache.hadoop.hbase.master.HMaster;
3134
import org.apache.hadoop.hbase.master.RegionState.State;
3235
import org.apache.hadoop.hbase.master.procedure.ProcedureSyncWait;
36+
import org.apache.hadoop.hbase.util.Bytes;
3337
import org.apache.hadoop.hbase.util.Threads;
3438
import org.apache.yetus.audience.InterfaceAudience;
3539
import org.apache.yetus.audience.InterfaceStability;
@@ -152,4 +156,25 @@ public static boolean waitForAssignment(AssignmentManager am, RegionInfo regionI
152156
proc, 5L * 60 * 1000);
153157
return true;
154158
}
159+
160+
public static void insertData(final HBaseTestingUtility UTIL, final TableName tableName,
161+
int rowCount, int startRowNum, String... cfs) throws IOException {
162+
Table t = UTIL.getConnection().getTable(tableName);
163+
Put p;
164+
for (int i = 0; i < rowCount / 2; i++) {
165+
p = new Put(Bytes.toBytes("" + (startRowNum + i)));
166+
for (String cf : cfs) {
167+
p.addColumn(Bytes.toBytes(cf), Bytes.toBytes("q"), Bytes.toBytes(i));
168+
}
169+
t.put(p);
170+
p = new Put(Bytes.toBytes("" + (startRowNum + rowCount - i - 1)));
171+
for (String cf : cfs) {
172+
p.addColumn(Bytes.toBytes(cf), Bytes.toBytes("q"), Bytes.toBytes(i));
173+
}
174+
t.put(p);
175+
if (i % 5 == 0) {
176+
UTIL.getAdmin().flush(tableName);
177+
}
178+
}
179+
}
155180
}

hbase-server/src/test/java/org/apache/hadoop/hbase/master/assignment/TestRegionSplit.java

Lines changed: 17 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -17,22 +17,24 @@
1717
*/
1818
package org.apache.hadoop.hbase.master.assignment;
1919

20+
import static org.apache.hadoop.hbase.master.assignment.AssignmentTestingUtil.insertData;
2021
import static org.junit.Assert.assertEquals;
2122
import static org.junit.Assert.assertTrue;
22-
import java.io.IOException;
23+
import java.util.List;
24+
import java.util.Map;
2325
import org.apache.hadoop.conf.Configuration;
2426
import org.apache.hadoop.hbase.HBaseClassTestRule;
2527
import org.apache.hadoop.hbase.HBaseTestingUtility;
28+
import org.apache.hadoop.hbase.ServerName;
2629
import org.apache.hadoop.hbase.StartMiniClusterOption;
2730
import org.apache.hadoop.hbase.TableName;
28-
import org.apache.hadoop.hbase.client.Put;
2931
import org.apache.hadoop.hbase.client.RegionInfo;
30-
import org.apache.hadoop.hbase.client.Table;
3132
import org.apache.hadoop.hbase.client.TableDescriptor;
3233
import org.apache.hadoop.hbase.master.procedure.MasterProcedureEnv;
3334
import org.apache.hadoop.hbase.master.procedure.MasterProcedureTestingUtility;
3435
import org.apache.hadoop.hbase.procedure2.ProcedureExecutor;
3536
import org.apache.hadoop.hbase.procedure2.ProcedureTestingUtility;
37+
import org.apache.hadoop.hbase.regionserver.HRegion;
3638
import org.apache.hadoop.hbase.testclassification.MasterTests;
3739
import org.apache.hadoop.hbase.testclassification.MediumTests;
3840
import org.apache.hadoop.hbase.util.Bytes;
@@ -60,7 +62,7 @@ public class TestRegionSplit {
6062

6163
protected static final HBaseTestingUtility UTIL = new HBaseTestingUtility();
6264

63-
private static String ColumnFamilyName = "cf";
65+
private static String columnFamilyName = "cf";
6466

6567
private static final int startRowNum = 11;
6668
private static final int rowCount = 60;
@@ -94,7 +96,8 @@ public void setup() throws Exception {
9496
UTIL.getHBaseCluster().getMaster().setCatalogJanitorEnabled(false);
9597
// Disable compaction.
9698
for (int i = 0; i < UTIL.getHBaseCluster().getLiveRegionServerThreads().size(); i++) {
97-
UTIL.getHBaseCluster().getRegionServer(i).getCompactSplitThread().switchCompaction(false);
99+
UTIL.getHBaseCluster().getRegionServer(i).getCompactSplitThread().switchCompaction(
100+
false);
98101
}
99102
}
100103

@@ -111,8 +114,8 @@ public void testSplitTableRegion() throws Exception {
111114
final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor();
112115

113116
RegionInfo[] regions =
114-
MasterProcedureTestingUtility.createTable(procExec, tableName, null, ColumnFamilyName);
115-
insertData(tableName);
117+
MasterProcedureTestingUtility.createTable(procExec, tableName, null, columnFamilyName);
118+
insertData(UTIL, tableName, rowCount, startRowNum, columnFamilyName);
116119
int splitRowNum = startRowNum + rowCount / 2;
117120
byte[] splitKey = Bytes.toBytes("" + splitRowNum);
118121

@@ -121,7 +124,7 @@ public void testSplitTableRegion() throws Exception {
121124

122125
// Split region of the table
123126
long procId = procExec.submitProcedure(
124-
new SplitTableRegionProcedure(procExec.getEnvironment(), regions[0], splitKey));
127+
new SplitTableRegionProcedure(procExec.getEnvironment(), regions[0], splitKey));
125128
// Wait the completion
126129
ProcedureTestingUtility.waitProcedure(procExec, procId);
127130
ProcedureTestingUtility.assertProcNotFailed(procExec, procId);
@@ -146,22 +149,12 @@ public void testSplitTableRegion() throws Exception {
146149
UTIL.getAdmin().enableTable(tableName);
147150
Thread.sleep(500);
148151

149-
assertEquals("Table region not correct.", 2,
150-
UTIL.getHBaseCluster().getRegions(tableName).size());
151-
}
152-
153-
private void insertData(final TableName tableName) throws IOException {
154-
Table t = UTIL.getConnection().getTable(tableName);
155-
Put p;
156-
for (int i = 0; i < rowCount / 2; i++) {
157-
p = new Put(Bytes.toBytes("" + (startRowNum + i)));
158-
p.addColumn(Bytes.toBytes(ColumnFamilyName), Bytes.toBytes("q1"), Bytes.toBytes(i));
159-
t.put(p);
160-
p = new Put(Bytes.toBytes("" + (startRowNum + rowCount - i - 1)));
161-
p.addColumn(Bytes.toBytes(ColumnFamilyName), Bytes.toBytes("q1"), Bytes.toBytes(i));
162-
t.put(p);
163-
}
164-
UTIL.getAdmin().flush(tableName);
152+
List<HRegion> tableRegions = UTIL.getHBaseCluster().getRegions(tableName);
153+
assertEquals("Table region not correct.", 2, tableRegions.size());
154+
Map<RegionInfo, ServerName> regionInfoMap = UTIL.getHBaseCluster().getMaster()
155+
.getAssignmentManager().getRegionStates().getRegionAssignments();
156+
assertEquals(regionInfoMap.get(tableRegions.get(0).getRegionInfo()),
157+
regionInfoMap.get(tableRegions.get(1).getRegionInfo()));
165158
}
166159

167160
private ProcedureExecutor<MasterProcedureEnv> getMasterProcedureExecutor() {

0 commit comments

Comments
 (0)