Skip to content

Commit 96a94ac

Browse files
shardul-cr7joshelser
authored andcommitted
HBASE-22142 Drop table RegionSizes with namespace quota
There was a bug in which we would not drop the RegionSizes for a table in a namespace, where the namespace had a quota on it. This allowed a scenario in which recreation of a table inside of a namespace would unintentionally move into violation despite the table being empty. Need to make sure the RegionSizes are dropped on table deletion if there is _any_ quota applying to that table. Closes #598 Signed-off-by: Josh Elser <[email protected]>
1 parent 2b3c9b1 commit 96a94ac

File tree

3 files changed

+95
-13
lines changed

3 files changed

+95
-13
lines changed

hbase-server/src/main/java/org/apache/hadoop/hbase/quotas/MasterQuotaManager.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -740,5 +740,14 @@ public void processFileArchivals(FileArchiveNotificationRequest request, Connect
740740
notifier.addArchivedFiles(filesWithSize);
741741
}
742742
}
743+
744+
/**
745+
* Removes each region size entry where the RegionInfo references the provided TableName.
746+
*
747+
* @param tableName tableName.
748+
*/
749+
public void removeRegionSizesForTable(TableName tableName) {
750+
regionSizes.keySet().removeIf(regionInfo -> regionInfo.getTable().equals(tableName));
751+
}
743752
}
744753

hbase-server/src/main/java/org/apache/hadoop/hbase/quotas/MasterQuotasObserver.java

Lines changed: 34 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -24,17 +24,22 @@
2424
import org.apache.hadoop.hbase.TableName;
2525
import org.apache.hadoop.hbase.client.Admin;
2626
import org.apache.hadoop.hbase.client.Connection;
27+
import org.apache.hadoop.hbase.coprocessor.CoprocessorException;
28+
import org.apache.hadoop.hbase.coprocessor.CoreCoprocessor;
29+
import org.apache.hadoop.hbase.coprocessor.HasMasterServices;
2730
import org.apache.hadoop.hbase.coprocessor.MasterCoprocessor;
2831
import org.apache.hadoop.hbase.coprocessor.MasterCoprocessorEnvironment;
2932
import org.apache.hadoop.hbase.coprocessor.MasterObserver;
3033
import org.apache.hadoop.hbase.coprocessor.ObserverContext;
34+
import org.apache.hadoop.hbase.master.MasterServices;
3135
import org.apache.yetus.audience.InterfaceAudience;
3236
import org.apache.hadoop.hbase.shaded.protobuf.generated.QuotaProtos.Quotas;
3337

3438
/**
3539
* An observer to automatically delete quotas when a table/namespace
3640
* is deleted.
3741
*/
42+
@CoreCoprocessor
3843
@InterfaceAudience.Private
3944
public class MasterQuotasObserver implements MasterCoprocessor, MasterObserver {
4045
public static final String REMOVE_QUOTA_ON_TABLE_DELETE = "hbase.quota.remove.on.table.delete";
@@ -43,6 +48,7 @@ public class MasterQuotasObserver implements MasterCoprocessor, MasterObserver {
4348
private CoprocessorEnvironment cpEnv;
4449
private Configuration conf;
4550
private boolean quotasEnabled = false;
51+
private MasterServices masterServices;
4652

4753
@Override
4854
public Optional<MasterObserver> getMasterObserver() {
@@ -51,9 +57,19 @@ public Optional<MasterObserver> getMasterObserver() {
5157

5258
@Override
5359
public void start(CoprocessorEnvironment ctx) throws IOException {
54-
this.cpEnv = ctx;
55-
this.conf = cpEnv.getConfiguration();
60+
this.conf = ctx.getConfiguration();
5661
this.quotasEnabled = QuotaUtil.isQuotaEnabled(conf);
62+
63+
if (!(ctx instanceof MasterCoprocessorEnvironment)) {
64+
throw new CoprocessorException("Must be loaded on master.");
65+
}
66+
// if running on master
67+
MasterCoprocessorEnvironment mEnv = (MasterCoprocessorEnvironment) ctx;
68+
if (mEnv instanceof HasMasterServices) {
69+
this.masterServices = ((HasMasterServices) mEnv).getMasterServices();
70+
} else {
71+
throw new CoprocessorException("Must be loaded on a master having master services.");
72+
}
5773
}
5874

5975
@Override
@@ -64,18 +80,23 @@ public void postDeleteTable(
6480
return;
6581
}
6682
final Connection conn = ctx.getEnvironment().getConnection();
67-
Quotas quotas = QuotaUtil.getTableQuota(conn, tableName);
68-
if (quotas != null){
69-
if (quotas.hasSpace()){
70-
QuotaSettings settings = QuotaSettingsFactory.removeTableSpaceLimit(tableName);
71-
try (Admin admin = conn.getAdmin()) {
72-
admin.setQuota(settings);
83+
Quotas tableQuotas = QuotaUtil.getTableQuota(conn, tableName);
84+
Quotas namespaceQuotas = QuotaUtil.getNamespaceQuota(conn, tableName.getNamespaceAsString());
85+
if (tableQuotas != null || namespaceQuotas != null) {
86+
// Remove regions of table from space quota map.
87+
this.masterServices.getMasterQuotaManager().removeRegionSizesForTable(tableName);
88+
if (tableQuotas != null) {
89+
if (tableQuotas.hasSpace()) {
90+
QuotaSettings settings = QuotaSettingsFactory.removeTableSpaceLimit(tableName);
91+
try (Admin admin = conn.getAdmin()) {
92+
admin.setQuota(settings);
93+
}
7394
}
74-
}
75-
if (quotas.hasThrottle()){
76-
QuotaSettings settings = QuotaSettingsFactory.unthrottleTable(tableName);
77-
try (Admin admin = conn.getAdmin()) {
78-
admin.setQuota(settings);
95+
if (tableQuotas.hasThrottle()) {
96+
QuotaSettings settings = QuotaSettingsFactory.unthrottleTable(tableName);
97+
try (Admin admin = conn.getAdmin()) {
98+
admin.setQuota(settings);
99+
}
79100
}
80101
}
81102
}

hbase-server/src/test/java/org/apache/hadoop/hbase/quotas/TestSpaceQuotaDropTable.java

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,23 @@
1515
*/
1616
package org.apache.hadoop.hbase.quotas;
1717

18+
import java.util.List;
19+
import java.util.Map;
1820
import java.util.concurrent.atomic.AtomicLong;
1921

2022
import org.apache.hadoop.conf.Configuration;
2123
import org.apache.hadoop.hbase.HBaseClassTestRule;
2224
import org.apache.hadoop.hbase.HBaseTestingUtility;
25+
import org.apache.hadoop.hbase.MetaTableAccessor;
2326
import org.apache.hadoop.hbase.TableName;
27+
import org.apache.hadoop.hbase.Waiter;
2428
import org.apache.hadoop.hbase.client.Put;
29+
import org.apache.hadoop.hbase.client.RegionInfo;
30+
import org.apache.hadoop.hbase.master.HMaster;
2531
import org.apache.hadoop.hbase.testclassification.LargeTests;
2632
import org.apache.hadoop.hbase.util.Bytes;
2733
import org.junit.AfterClass;
34+
import org.junit.Assert;
2835
import org.junit.Before;
2936
import org.junit.BeforeClass;
3037
import org.junit.ClassRule;
@@ -87,6 +94,51 @@ public void testSetQuotaAndThenDropTableWithDisable() throws Exception {
8794
setQuotaAndThenDropTable(SpaceViolationPolicy.DISABLE);
8895
}
8996

97+
@Test
98+
public void testSetQuotaAndThenDropTableWithRegionReport() throws Exception {
99+
final TableName tn = helper.createTable();
100+
helper.setQuotaLimit(tn, SpaceViolationPolicy.NO_INSERTS, 1L);
101+
helper.writeData(tn, 2L);
102+
103+
final HMaster master = TEST_UTIL.getMiniHBaseCluster().getMaster();
104+
final MasterQuotaManager quotaManager = master.getMasterQuotaManager();
105+
106+
// Make sure the master has report for the table.
107+
Waiter.waitFor(TEST_UTIL.getConfiguration(), 30 * 1000, new Waiter.Predicate<Exception>() {
108+
@Override
109+
public boolean evaluate() throws Exception {
110+
Map<RegionInfo, Long> regionSizes = quotaManager.snapshotRegionSizes();
111+
List<RegionInfo> tableRegions =
112+
MetaTableAccessor.getTableRegions(TEST_UTIL.getConnection(), tn);
113+
return regionSizes.containsKey(tableRegions.get(0));
114+
}
115+
});
116+
117+
boolean hasRegionSize = false;
118+
119+
// region report should be present before dropping the table.
120+
for (Map.Entry<RegionInfo, Long> entry : quotaManager.snapshotRegionSizes().entrySet()) {
121+
if (entry.getKey().getTable().equals(tn)) {
122+
hasRegionSize = true;
123+
break;
124+
}
125+
}
126+
127+
// regionSize report for the given table should be present before dropping the table.
128+
Assert.assertTrue(hasRegionSize);
129+
130+
// drop the table
131+
TEST_UTIL.getAdmin().disableTable(tn);
132+
TEST_UTIL.getAdmin().deleteTable(tn);
133+
134+
// check if deleted table region report still present in the map.
135+
for (Map.Entry<RegionInfo, Long> entry : quotaManager.snapshotRegionSizes().entrySet()) {
136+
if (entry.getKey().getTable().equals(tn)) {
137+
Assert.fail("Dropped table regionSizes were not deleted during the drop command");
138+
}
139+
}
140+
}
141+
90142
private void setQuotaAndThenDropTable(SpaceViolationPolicy policy) throws Exception {
91143
Put put = new Put(Bytes.toBytes("to_reject"));
92144
put.addColumn(Bytes.toBytes(SpaceQuotaHelperForTests.F1), Bytes.toBytes("to"),

0 commit comments

Comments
 (0)