Skip to content

Commit d93bf76

Browse files
committed
HBASE-22809 Allow creating table in group when rs group contains no live servers (#464)
Signed-off-by: Guanghao Zhang <[email protected]>
1 parent 82db37a commit d93bf76

File tree

3 files changed

+71
-94
lines changed

3 files changed

+71
-94
lines changed

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

Lines changed: 68 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,12 @@
2121
import java.io.IOException;
2222
import java.util.Collections;
2323
import java.util.List;
24+
import java.util.Objects;
2425
import java.util.Optional;
2526
import java.util.Set;
27+
import java.util.function.Supplier;
2628
import java.util.stream.Collectors;
2729
import org.apache.hadoop.hbase.CoprocessorEnvironment;
28-
import org.apache.hadoop.hbase.HBaseIOException;
2930
import org.apache.hadoop.hbase.HConstants;
3031
import org.apache.hadoop.hbase.NamespaceDescriptor;
3132
import org.apache.hadoop.hbase.ServerName;
@@ -68,7 +69,7 @@ public void start(CoprocessorEnvironment env) throws IOException {
6869
groupInfoManager = RSGroupInfoManagerImpl.getInstance(master);
6970
groupAdminServer = new RSGroupAdminServer(master, groupInfoManager);
7071
Class<?> clazz =
71-
master.getConfiguration().getClass(HConstants.HBASE_MASTER_LOADBALANCER_CLASS, null);
72+
master.getConfiguration().getClass(HConstants.HBASE_MASTER_LOADBALANCER_CLASS, null);
7273
if (!RSGroupableBalancer.class.isAssignableFrom(clazz)) {
7374
throw new IOException("Configured balancer does not support RegionServer groups.");
7475
}
@@ -108,85 +109,101 @@ RSGroupAdminServiceImpl getGroupAdminService() {
108109

109110
@Override
110111
public void postClearDeadServers(ObserverContext<MasterCoprocessorEnvironment> ctx,
111-
List<ServerName> servers, List<ServerName> notClearedServers) throws IOException {
112+
List<ServerName> servers, List<ServerName> notClearedServers) throws IOException {
112113
Set<Address> clearedServer =
113-
servers.stream().filter(server -> !notClearedServers.contains(server))
114-
.map(ServerName::getAddress).collect(Collectors.toSet());
114+
servers.stream().filter(server -> !notClearedServers.contains(server))
115+
.map(ServerName::getAddress).collect(Collectors.toSet());
115116
if (!clearedServer.isEmpty()) {
116117
groupAdminServer.removeServers(clearedServer);
117118
}
118119
}
119120

120-
private void checkGroupExists(Optional<String> optGroupName) throws IOException {
121+
private RSGroupInfo checkGroupExists(Optional<String> optGroupName, Supplier<String> forWhom)
122+
throws IOException {
121123
if (optGroupName.isPresent()) {
122124
String groupName = optGroupName.get();
123-
if (groupAdminServer.getRSGroupInfo(groupName) == null) {
124-
throw new ConstraintException("Region server group " + groupName + " does not exit");
125+
RSGroupInfo group = groupAdminServer.getRSGroupInfo(groupName);
126+
if (group == null) {
127+
throw new ConstraintException(
128+
"Region server group " + groupName + " for " + forWhom.get() + " does not exit");
125129
}
130+
return group;
126131
}
132+
return null;
127133
}
128134

129-
private boolean rsgroupHasServersOnline(TableDescriptor desc) throws IOException {
130-
RSGroupInfo rsGroupInfo;
131-
Optional<String> optGroupName = desc.getRegionServerGroup();
132-
if (optGroupName.isPresent()) {
133-
String groupName = optGroupName.get();
134-
if (groupName.equals(RSGroupInfo.DEFAULT_GROUP)) {
135-
// do not check for default group
136-
return true;
137-
}
138-
rsGroupInfo = groupAdminServer.getRSGroupInfo(groupName);
139-
if (rsGroupInfo == null) {
140-
throw new ConstraintException(
141-
"RSGroup " + groupName + " for table " + desc.getTableName() + " does not exist");
142-
}
143-
} else {
144-
NamespaceDescriptor nd =
145-
master.getClusterSchema().getNamespace(desc.getTableName().getNamespaceAsString());
146-
String groupNameOfNs = nd.getConfigurationValue(RSGroupInfo.NAMESPACE_DESC_PROP_GROUP);
147-
if (groupNameOfNs == null || groupNameOfNs.equals(RSGroupInfo.DEFAULT_GROUP)) {
148-
// do not check for default group
149-
return true;
150-
}
151-
rsGroupInfo = groupAdminServer.getRSGroupInfo(groupNameOfNs);
152-
if (rsGroupInfo == null) {
153-
throw new ConstraintException("RSGroup " + groupNameOfNs + " for table " +
154-
desc.getTableName() + "(inherit from namespace) does not exist");
155-
}
135+
private Optional<String> getNamespaceGroup(NamespaceDescriptor namespaceDesc) {
136+
return Optional
137+
.ofNullable(namespaceDesc.getConfigurationValue(RSGroupInfo.NAMESPACE_DESC_PROP_GROUP));
138+
}
139+
140+
// Do not allow creating new tables/namespaces which has an empty rs group, expect the default rs
141+
// group. Notice that we do not check for online servers, as this is not stable because region
142+
// servers can die at any time.
143+
private void checkGroupNotEmpty(RSGroupInfo rsGroupInfo, Supplier<String> forWhom)
144+
throws ConstraintException {
145+
if (rsGroupInfo == null || rsGroupInfo.getName().equals(RSGroupInfo.DEFAULT_GROUP)) {
146+
// we do not have a rs group config or we explicitly set the rs group to default, then no need
147+
// to check.
148+
return;
149+
}
150+
if (rsGroupInfo.getServers().isEmpty()) {
151+
throw new ConstraintException(
152+
"No servers in the rsgroup " + rsGroupInfo.getName() + " for " + forWhom.get());
156153
}
157-
return master.getServerManager().createDestinationServersList().stream()
158-
.anyMatch(onlineServer -> rsGroupInfo.containsServer(onlineServer.getAddress()));
159154
}
160155

161156
@Override
162157
public void preCreateTableAction(ObserverContext<MasterCoprocessorEnvironment> ctx,
163-
TableDescriptor desc, RegionInfo[] regions) throws IOException {
164-
checkGroupExists(desc.getRegionServerGroup());
165-
if (!desc.getTableName().isSystemTable() && !rsgroupHasServersOnline(desc)) {
166-
throw new HBaseIOException("No online servers in the rsgroup for " + desc);
158+
TableDescriptor desc, RegionInfo[] regions) throws IOException {
159+
if (desc.getTableName().isSystemTable()) {
160+
// do not check for system tables as we may block the bootstrap.
161+
return;
162+
}
163+
Supplier<String> forWhom = () -> "table " + desc.getTableName();
164+
RSGroupInfo rsGroupInfo = checkGroupExists(desc.getRegionServerGroup(), forWhom);
165+
if (rsGroupInfo == null) {
166+
// we do not set rs group info on table, check if we have one on namespace
167+
String namespace = desc.getTableName().getNamespaceAsString();
168+
NamespaceDescriptor nd = master.getClusterSchema().getNamespace(namespace);
169+
forWhom = () -> "table " + desc.getTableName() + "(inherit from namespace)";
170+
rsGroupInfo = checkGroupExists(getNamespaceGroup(nd), forWhom);
167171
}
172+
checkGroupNotEmpty(rsGroupInfo, forWhom);
168173
}
169174

170175
@Override
171176
public TableDescriptor preModifyTable(ObserverContext<MasterCoprocessorEnvironment> ctx,
172-
TableName tableName, TableDescriptor currentDescriptor, TableDescriptor newDescriptor)
173-
throws IOException {
174-
checkGroupExists(newDescriptor.getRegionServerGroup());
177+
TableName tableName, TableDescriptor currentDescriptor, TableDescriptor newDescriptor)
178+
throws IOException {
179+
if (!currentDescriptor.getRegionServerGroup().equals(newDescriptor.getRegionServerGroup())) {
180+
Supplier<String> forWhom = () -> "table " + newDescriptor.getTableName();
181+
RSGroupInfo rsGroupInfo = checkGroupExists(newDescriptor.getRegionServerGroup(), forWhom);
182+
checkGroupNotEmpty(rsGroupInfo, forWhom);
183+
}
175184
return MasterObserver.super.preModifyTable(ctx, tableName, currentDescriptor, newDescriptor);
176185
}
177186

187+
private void checkNamespaceGroup(NamespaceDescriptor nd) throws IOException {
188+
Supplier<String> forWhom = () -> "namespace " + nd.getName();
189+
RSGroupInfo rsGroupInfo = checkGroupExists(getNamespaceGroup(nd), forWhom);
190+
checkGroupNotEmpty(rsGroupInfo, forWhom);
191+
}
192+
178193
@Override
179194
public void preCreateNamespace(ObserverContext<MasterCoprocessorEnvironment> ctx,
180-
NamespaceDescriptor ns) throws IOException {
181-
checkGroupExists(
182-
Optional.ofNullable(ns.getConfigurationValue(RSGroupInfo.NAMESPACE_DESC_PROP_GROUP)));
195+
NamespaceDescriptor ns) throws IOException {
196+
checkNamespaceGroup(ns);
183197
}
184198

185199
@Override
186200
public void preModifyNamespace(ObserverContext<MasterCoprocessorEnvironment> ctx,
187-
NamespaceDescriptor currentNsDescriptor, NamespaceDescriptor newNsDescriptor)
188-
throws IOException {
189-
checkGroupExists(Optional
190-
.ofNullable(newNsDescriptor.getConfigurationValue(RSGroupInfo.NAMESPACE_DESC_PROP_GROUP)));
201+
NamespaceDescriptor currentNsDescriptor, NamespaceDescriptor newNsDescriptor)
202+
throws IOException {
203+
if (!Objects.equals(
204+
currentNsDescriptor.getConfigurationValue(RSGroupInfo.NAMESPACE_DESC_PROP_GROUP),
205+
newNsDescriptor.getConfigurationValue(RSGroupInfo.NAMESPACE_DESC_PROP_GROUP))) {
206+
checkNamespaceGroup(newNsDescriptor);
207+
}
191208
}
192209
}

hbase-server/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupsAdmin1.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,12 +152,14 @@ public void testNamespaceConstraint() throws Exception {
152152
String nsName = tablePrefix + "_foo";
153153
String groupName = tablePrefix + "_foo";
154154
LOG.info("testNamespaceConstraint");
155-
rsGroupAdmin.addRSGroup(groupName);
155+
addGroup(groupName, 1);
156156
assertTrue(observer.preAddRSGroupCalled);
157157
assertTrue(observer.postAddRSGroupCalled);
158158

159159
admin.createNamespace(NamespaceDescriptor.create(nsName)
160160
.addConfiguration(RSGroupInfo.NAMESPACE_DESC_PROP_GROUP, groupName).build());
161+
RSGroupInfo rsGroupInfo = rsGroupAdmin.getRSGroupInfo(groupName);
162+
rsGroupAdmin.moveServers(rsGroupInfo.getServers(), RSGroupInfo.DEFAULT_GROUP);
161163
// test removing a referenced group
162164
try {
163165
rsGroupAdmin.removeRSGroup(groupName);

hbase-server/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupsBasics.java

Lines changed: 0 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,8 @@
2020
import static org.junit.Assert.assertEquals;
2121
import static org.junit.Assert.assertFalse;
2222
import static org.junit.Assert.assertTrue;
23-
import static org.junit.Assert.fail;
2423

2524
import java.io.IOException;
26-
import java.util.ArrayList;
27-
import java.util.Iterator;
2825
import java.util.List;
2926
import java.util.Set;
3027
import org.apache.hadoop.hbase.HBaseClassTestRule;
@@ -136,45 +133,6 @@ public boolean evaluate() throws Exception {
136133
Assert.assertEquals(1, admin.getRegions(targetServer).size());
137134
}
138135

139-
@Test
140-
public void testCreateWhenRsgroupNoOnlineServers() throws Exception {
141-
LOG.info("testCreateWhenRsgroupNoOnlineServers");
142-
143-
// set rsgroup has no online servers and test create table
144-
final RSGroupInfo appInfo = addGroup("appInfo", 1);
145-
Iterator<Address> iterator = appInfo.getServers().iterator();
146-
List<ServerName> serversToDecommission = new ArrayList<>();
147-
ServerName targetServer = getServerName(iterator.next());
148-
assertTrue(master.getServerManager().getOnlineServers().containsKey(targetServer));
149-
serversToDecommission.add(targetServer);
150-
admin.decommissionRegionServers(serversToDecommission, true);
151-
assertEquals(1, admin.listDecommissionedRegionServers().size());
152-
153-
final TableName tableName = TableName.valueOf(tablePrefix + "_ns", name.getMethodName());
154-
admin.createNamespace(NamespaceDescriptor.create(tableName.getNamespaceAsString())
155-
.addConfiguration(RSGroupInfo.NAMESPACE_DESC_PROP_GROUP, appInfo.getName()).build());
156-
final TableDescriptor desc = TableDescriptorBuilder.newBuilder(tableName)
157-
.setColumnFamily(ColumnFamilyDescriptorBuilder.of("f")).build();
158-
try {
159-
admin.createTable(desc);
160-
fail("Shouldn't create table successfully!");
161-
} catch (Exception e) {
162-
LOG.debug("create table error", e);
163-
}
164-
165-
// recommission and test create table
166-
admin.recommissionRegionServer(targetServer, null);
167-
assertEquals(0, admin.listDecommissionedRegionServers().size());
168-
admin.createTable(desc);
169-
// wait for created table to be assigned
170-
TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
171-
@Override
172-
public boolean evaluate() throws Exception {
173-
return getTableRegionMap().get(desc.getTableName()) != null;
174-
}
175-
});
176-
}
177-
178136
@Test
179137
public void testDefaultNamespaceCreateAndAssign() throws Exception {
180138
LOG.info("testDefaultNamespaceCreateAndAssign");

0 commit comments

Comments
 (0)