Skip to content

Commit 557d4a7

Browse files
HBASE-25492: Create table with rsgroup
1 parent f391a2b commit 557d4a7

8 files changed

Lines changed: 373 additions & 137 deletions

File tree

hbase-client/src/main/java/org/apache/hadoop/hbase/HTableDescriptor.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import java.util.Collection;
2323
import java.util.Collections;
2424
import java.util.Map;
25+
import java.util.Optional;
2526
import java.util.Set;
2627
import java.util.stream.Collectors;
2728
import java.util.stream.Stream;
@@ -987,4 +988,9 @@ public ColumnFamilyDescriptor getColumnFamily(byte[] name) {
987988
protected ModifyableTableDescriptor getDelegateeForModification() {
988989
return delegatee;
989990
}
991+
992+
@Override
993+
public Optional<String> getRegionServerGroup() {
994+
return delegatee.getRegionServerGroup();
995+
}
990996
}

hbase-client/src/main/java/org/apache/hadoop/hbase/client/TableDescriptor.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import java.util.Comparator;
2424
import java.util.Iterator;
2525
import java.util.Map;
26+
import java.util.Optional;
2627
import java.util.Set;
2728
import java.util.stream.Collectors;
2829
import java.util.stream.Stream;
@@ -360,4 +361,11 @@ default boolean matchReplicationScope(boolean enabled) {
360361
}
361362
return !enabled;
362363
}
364+
365+
/**
366+
* Get the region server group this table belongs to. The regions of this table will be placed
367+
* only on the region servers within this group. If not present, will be placed on
368+
* {@link org.apache.hadoop.hbase.rsgroup.RSGroupInfo#DEFAULT_GROUP}.
369+
*/
370+
Optional<String> getRegionServerGroup();
363371
}

hbase-client/src/main/java/org/apache/hadoop/hbase/client/TableDescriptorBuilder.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
import org.apache.hadoop.hbase.TableName;
4242
import org.apache.hadoop.hbase.exceptions.DeserializationException;
4343
import org.apache.hadoop.hbase.exceptions.HBaseException;
44+
import org.apache.hadoop.hbase.rsgroup.RSGroupInfo;
4445
import org.apache.hadoop.hbase.security.User;
4546
import org.apache.hadoop.hbase.util.Bytes;
4647
import org.apache.hadoop.hbase.util.PrettyPrinter;
@@ -192,6 +193,9 @@ public class TableDescriptorBuilder {
192193
private static final Bytes PRIORITY_KEY
193194
= new Bytes(Bytes.toBytes(PRIORITY));
194195

196+
private static final Bytes RSGROUP_KEY =
197+
new Bytes(Bytes.toBytes(RSGroupInfo.TABLE_DESC_PROP_GROUP));
198+
195199
/**
196200
* Relative priority of the table used for rpc scheduling
197201
*/
@@ -594,6 +598,11 @@ public TableDescriptorBuilder setReplicationScope(int scope) {
594598
return this;
595599
}
596600

601+
public TableDescriptorBuilder setRegionServerGroup(String group) {
602+
desc.setValue(RSGROUP_KEY, group);
603+
return this;
604+
}
605+
597606
public TableDescriptor build() {
598607
return new ModifyableTableDescriptor(desc);
599608
}
@@ -1647,6 +1656,16 @@ private static TableDescriptor parseFrom(final byte[] bytes)
16471656
public int getColumnFamilyCount() {
16481657
return families.size();
16491658
}
1659+
1660+
@Override
1661+
public Optional<String> getRegionServerGroup() {
1662+
Bytes value = values.get(RSGROUP_KEY);
1663+
if (value != null) {
1664+
return Optional.of(Bytes.toString(value.get(), value.getOffset(), value.getLength()));
1665+
} else {
1666+
return Optional.empty();
1667+
}
1668+
}
16501669
}
16511670

16521671
private static Optional<CoprocessorDescriptor> toCoprocessorDescriptor(String spec) {

hbase-common/src/main/java/org/apache/hadoop/hbase/rsgroup/RSGroupInfo.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
public class RSGroupInfo {
3939
public static final String DEFAULT_GROUP = "default";
4040
public static final String NAMESPACE_DESC_PROP_GROUP = "hbase.rsgroup.name";
41+
public static final String TABLE_DESC_PROP_GROUP = "hbase.rsgroup.name";
4142

4243
private final String name;
4344
// Keep servers in a sorted set so has an expected ordering when displayed.

hbase-rsgroup/src/main/java/org/apache/hadoop/hbase/rsgroup/RSGroupAdminEndpoint.java

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -485,7 +485,7 @@ boolean rsgroupHasServersOnline(TableDescriptor desc) throws IOException {
485485
}
486486

487487
void assignTableToGroup(TableDescriptor desc) throws IOException {
488-
RSGroupInfo rsGroupInfo = groupInfoManager.determineRSGroupInfoForTable(desc.getTableName());
488+
RSGroupInfo rsGroupInfo = determineRSGroupInfoForTable(desc);
489489
if (rsGroupInfo == null) {
490490
throw new ConstraintException("Default RSGroup for this table " + desc.getTableName()
491491
+ " does not exist.");
@@ -508,7 +508,7 @@ public void preCreateTableAction(
508508
if (desc.getTableName().isSystemTable()) {
509509
return;
510510
}
511-
RSGroupInfo rsGroupInfo = groupInfoManager.determineRSGroupInfoForTable(desc.getTableName());
511+
RSGroupInfo rsGroupInfo = determineRSGroupInfoForTable(desc);
512512
if (rsGroupInfo == null) {
513513
throw new ConstraintException("Default RSGroup for this table " + desc.getTableName()
514514
+ " does not exist.");
@@ -523,6 +523,20 @@ public void preCreateTableAction(
523523
}
524524
}
525525

526+
private RSGroupInfo determineRSGroupInfoForTable(final TableDescriptor desc) throws IOException {
527+
Optional<String> optGroupNameOfTable = desc.getRegionServerGroup();
528+
if (optGroupNameOfTable.isPresent()) {
529+
final RSGroupInfo rsGroup = groupInfoManager.getRSGroup(optGroupNameOfTable.get());
530+
if (rsGroup == null) {
531+
// When rs group is set in table descriptor then it must exist
532+
throw new ConstraintException(
533+
"Region server group " + optGroupNameOfTable.get() + " does not exist.");
534+
} else {
535+
return rsGroup;
536+
}
537+
}
538+
return groupInfoManager.determineRSGroupInfoForTable(desc.getTableName());
539+
}
526540
// Remove table from its RSGroup.
527541
@Override
528542
public void postDeleteTable(ObserverContext<MasterCoprocessorEnvironment> ctx,

hbase-rsgroup/src/main/java/org/apache/hadoop/hbase/rsgroup/RSGroupAdminServer.java

Lines changed: 34 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,15 @@
2727
import java.util.Map;
2828
import java.util.Set;
2929
import java.util.concurrent.Future;
30+
import java.util.stream.Collectors;
3031
import org.apache.commons.lang3.StringUtils;
32+
import org.apache.hadoop.hbase.HConstants;
3133
import org.apache.hadoop.hbase.NamespaceDescriptor;
3234
import org.apache.hadoop.hbase.ServerName;
3335
import org.apache.hadoop.hbase.TableName;
3436
import org.apache.hadoop.hbase.client.RegionInfo;
37+
import org.apache.hadoop.hbase.client.TableDescriptor;
38+
import org.apache.hadoop.hbase.client.TableDescriptorBuilder;
3539
import org.apache.hadoop.hbase.client.TableState;
3640
import org.apache.hadoop.hbase.constraint.ConstraintException;
3741
import org.apache.hadoop.hbase.master.HMaster;
@@ -43,7 +47,9 @@
4347
import org.apache.hadoop.hbase.master.TableStateManager;
4448
import org.apache.hadoop.hbase.master.assignment.AssignmentManager;
4549
import org.apache.hadoop.hbase.master.assignment.RegionStateNode;
50+
import org.apache.hadoop.hbase.master.procedure.ProcedureSyncWait;
4651
import org.apache.hadoop.hbase.net.Address;
52+
import org.apache.hadoop.hbase.procedure2.Procedure;
4753
import org.apache.hadoop.hbase.util.Pair;
4854
import org.apache.hbase.thirdparty.com.google.common.collect.Maps;
4955
import org.apache.yetus.audience.InterfaceAudience;
@@ -302,78 +308,6 @@ private boolean waitForRegionMovement(List<Pair<RegionInfo, Future<byte[]>>> reg
302308
return allRegionsMoved;
303309
}
304310

305-
/**
306-
* Moves regions of tables which are not on target group servers.
307-
*
308-
* @param tables the tables that will move to new group
309-
* @param targetGrp the target group
310-
* @throws IOException if moving the region fails
311-
*/
312-
private void moveTableRegionsToGroup(Set<TableName> tables, RSGroupInfo targetGrp)
313-
throws IOException {
314-
List<ServerName> targetGrpSevers = new ArrayList<>(targetGrp.getServers().size());
315-
for (ServerName serverName : master.getServerManager().getOnlineServers().keySet()) {
316-
if (targetGrp.getServers().contains(serverName.getAddress())) {
317-
targetGrpSevers.add(serverName);
318-
}
319-
}
320-
//Set true to indicate at least one region movement failed
321-
boolean errorInRegionMove;
322-
int retry = 0;
323-
List<Pair<RegionInfo, Future<byte[]>>> assignmentFutures = new ArrayList<>();
324-
do {
325-
errorInRegionMove = false;
326-
for (TableName table : tables) {
327-
if (master.getTableStateManager().isTableState(table, TableState.State.DISABLED,
328-
TableState.State.DISABLING)) {
329-
LOG.debug("Skipping move regions because the table {} is disabled", table);
330-
continue;
331-
}
332-
LOG.info("Moving region(s) for table {} to RSGroup {}", table, targetGrp.getName());
333-
for (RegionInfo region : master.getAssignmentManager().getRegionStates()
334-
.getRegionsOfTable(table)) {
335-
ServerName sn =
336-
master.getAssignmentManager().getRegionStates().getRegionServerOfRegion(region);
337-
if (!targetGrp.containsServer(sn.getAddress())) {
338-
LOG.info("Moving region {} to RSGroup {}", region.getShortNameToLog(),
339-
targetGrp.getName());
340-
ServerName dest =
341-
this.master.getLoadBalancer().randomAssignment(region, targetGrpSevers);
342-
if (dest == null) {
343-
errorInRegionMove = true;
344-
continue;
345-
}
346-
RegionPlan rp = new RegionPlan(region, sn, dest);
347-
try {
348-
Future<byte[]> future = this.master.getAssignmentManager().moveAsync(rp);
349-
assignmentFutures.add(Pair.newPair(region, future));
350-
} catch (Exception ioe) {
351-
errorInRegionMove = true;
352-
LOG.error("Move region {} to group failed, will retry, current retry time is {}",
353-
region.getShortNameToLog(), retry, ioe);
354-
}
355-
356-
}
357-
}
358-
}
359-
boolean allRegionsMoved =
360-
waitForRegionMovement(assignmentFutures, targetGrp.getName(), retry);
361-
if (allRegionsMoved && !errorInRegionMove) {
362-
LOG.info("All regions from table(s) {} moved to target group {}.", tables,
363-
targetGrp.getName());
364-
return;
365-
} else {
366-
retry++;
367-
try {
368-
rsGroupInfoManager.wait(1000);
369-
} catch (InterruptedException e) {
370-
LOG.warn("Sleep interrupted", e);
371-
Thread.currentThread().interrupt();
372-
}
373-
}
374-
} while (retry <= 50);
375-
}
376-
377311
@edu.umd.cs.findbugs.annotations.SuppressWarnings(
378312
value="RCN_REDUNDANT_NULLCHECK_WOULD_HAVE_BEEN_A_NPE",
379313
justification="Ignoring complaint because don't know what it is complaining about")
@@ -459,7 +393,7 @@ public void moveTables(Set<TableName> tables, String targetGroup) throws IOExcep
459393
// targetGroup is null when a table is being deleted. In this case no further
460394
// action is required.
461395
if (targetGroup != null) {
462-
moveTableRegionsToGroup(tables, rsGroupInfoManager.getRSGroup(targetGroup));
396+
moveTablesAndWait(tables, targetGroup);
463397
}
464398
}
465399
}
@@ -582,7 +516,7 @@ public void moveServersAndTables(Set<Address> servers, Set<TableName> tables, St
582516
rsGroupInfoManager.getRSGroup(srcGroup).getServers(),
583517
targetGroup, srcGroup);
584518
//move regions of these tables which are not on group servers
585-
moveTableRegionsToGroup(tables, rsGroupInfoManager.getRSGroup(targetGroup));
519+
moveTablesAndWait(tables, targetGroup);
586520
}
587521
LOG.info("Move servers and tables done. Severs: {}, Tables: {} => {}", servers, tables,
588522
targetGroup);
@@ -609,6 +543,11 @@ public void removeServers(Set<Address> servers) throws IOException {
609543
public void renameRSGroup(String oldName, String newName) throws IOException {
610544
synchronized (rsGroupInfoManager) {
611545
rsGroupInfoManager.renameRSGroup(oldName, newName);
546+
Set<TableName> updateTables = master.getTableDescriptors().getAll().values().stream()
547+
.filter(t -> oldName.equals(t.getRegionServerGroup().orElse(null)))
548+
.map(TableDescriptor::getTableName).collect(Collectors.toSet());
549+
// Update rs group info into table descriptors, here move is not relevant
550+
moveTablesAndWait(updateTables, newName);
612551
}
613552
}
614553

@@ -718,4 +657,25 @@ private void checkForDeadOrOnlineServers(Set<Address> servers) throws Constraint
718657
}
719658
}
720659
}
660+
661+
private void moveTablesAndWait(Set<TableName> tables, String targetGroup) throws IOException {
662+
List<Long> procIds = new ArrayList<>();
663+
for (TableName tableName : tables) {
664+
TableDescriptor oldTd = master.getTableDescriptors().get(tableName);
665+
if (oldTd == null) {
666+
continue;
667+
}
668+
TableDescriptor newTd =
669+
TableDescriptorBuilder.newBuilder(oldTd).setRegionServerGroup(targetGroup).build();
670+
procIds.add(master.modifyTable(tableName, newTd, HConstants.NO_NONCE, HConstants.NO_NONCE));
671+
}
672+
for (long procId : procIds) {
673+
Procedure<?> proc = master.getMasterProcedureExecutor().getProcedure(procId);
674+
if (proc == null) {
675+
continue;
676+
}
677+
ProcedureSyncWait
678+
.waitForProcedureToCompleteIOE(master.getMasterProcedureExecutor(), proc, Long.MAX_VALUE);
679+
}
680+
}
721681
}

hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupsAdmin2.java

Lines changed: 28 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -578,74 +578,41 @@ public boolean evaluate() throws Exception {
578578
}
579579
}
580580
assert srcServer != null;
581-
RegionInfo ri = TEST_UTIL.getMiniHBaseCluster().getMaster().getAssignmentManager().
582-
getRegionInfo(Bytes.toBytesBinary(rregion));
583-
RegionStateNode rsn =
584-
TEST_UTIL.getMiniHBaseCluster().getMaster().getAssignmentManager().getRegionStates()
585-
.getRegionStateNode(ri);
581+
RegionInfo ri = master.getAssignmentManager().
582+
getRegionInfo(Bytes.toBytesBinary(rregion));
583+
RegionStateNode rsn = master.getAssignmentManager().getRegionStates().getRegionStateNode(ri);
586584
rsn.setState(RegionState.State.SPLITTING);
587585

588-
// move table to group
589-
Thread t2 = new Thread(() -> {
590-
LOG.info("thread2 start running, to move regions");
591-
try {
592-
rsGroupAdmin.moveTables(Sets.newHashSet(tableName), newGroup.getName());
593-
} catch (IOException e) {
594-
LOG.error("move server error", e);
595-
}
596-
});
597-
t2.start();
586+
rsGroupAdmin.moveTables(Sets.newHashSet(tableName), newGroup.getName());
598587

599-
// start thread to recover region state
600-
final ServerName ss = srcServer;
601-
final String sregion = rregion;
602-
AtomicBoolean changed = new AtomicBoolean(false);
603-
Thread t1 = new Thread(() -> {
604-
LOG.info("thread1 start running, will recover region state");
605-
long current = System.currentTimeMillis();
606-
while (System.currentTimeMillis() - current <= 50000) {
607-
List<RegionInfo> regions = master.getAssignmentManager().getRegionsOnServer(ss);
608-
List<RegionInfo> tableRegions = new ArrayList<>();
609-
for (RegionInfo regionInfo : regions) {
610-
if (regionInfo.getTable().equals(tableName)) {
611-
tableRegions.add(regionInfo);
612-
}
588+
// Except splitting region all must have moved.
589+
final Address groupServer = newGroup.getServers().iterator().next();
590+
for (RegionInfo regionInfo : master.getAssignmentManager().getAssignedRegions()) {
591+
if (regionInfo.getTable().equals(tableName)) {
592+
if (regionInfo.equals(rsn.getRegionInfo())) {
593+
// This is splitting region
594+
assertEquals(
595+
master.getAssignmentManager().getRegionStates().getRegionServerOfRegion(regionInfo),
596+
srcServer);
597+
} else {
598+
// These are normal regions
599+
assertEquals(
600+
master.getAssignmentManager().getRegionStates().getRegionServerOfRegion(regionInfo)
601+
.getAddress(), groupServer);
613602
}
614-
LOG.debug("server table region size is:{}", tableRegions.size());
615-
assert tableRegions.size() >= 1;
616-
// when there is exactly one region left, we can determine the move operation encountered
617-
// exception caused by the strange region state.
618-
if (tableRegions.size() == 1) {
619-
assertEquals(tableRegions.get(0).getRegionNameAsString(), sregion);
620-
rsn.setState(RegionState.State.OPEN);
621-
LOG.info("set region {} state OPEN", sregion);
622-
changed.set(true);
623-
break;
624-
}
625-
sleep(5000);
626603
}
627-
});
628-
t1.start();
629-
630-
t1.join();
631-
t2.join();
604+
}
632605

633-
TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
634-
@Override
635-
public boolean evaluate() {
636-
if (changed.get()) {
637-
boolean serverHasTableRegions = false;
638-
for (RegionInfo regionInfo : master.getAssignmentManager().getRegionsOnServer(ss)) {
639-
if (regionInfo.getTable().equals(tableName)) {
640-
serverHasTableRegions = true;
641-
break;
642-
}
643-
}
644-
return !serverHasTableRegions && !rsn.getRegionLocation().equals(ss);
645-
}
646-
return false;
606+
rsn.setState(RegionState.State.OPEN);
607+
// Retry table movement
608+
rsGroupAdmin.moveTables(Sets.newHashSet(tableName), newGroup.getName());
609+
for (RegionInfo regionInfo : master.getAssignmentManager().getAssignedRegions()) {
610+
if (regionInfo.getTable().equals(tableName)) {
611+
assertEquals(
612+
master.getAssignmentManager().getRegionStates().getRegionServerOfRegion(regionInfo)
613+
.getAddress(), groupServer);
647614
}
648-
});
615+
}
649616
}
650617

651618
@Test

0 commit comments

Comments
 (0)