-
Notifications
You must be signed in to change notification settings - Fork 3.4k
HBASE-22142 Space quota: If table inside namespace having space quota is dropped, data size usage is still considered for the drop table. #598
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -740,5 +740,14 @@ public void processFileArchivals(FileArchiveNotificationRequest request, Connect | |
| notifier.addArchivedFiles(filesWithSize); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Remove regionInfo entry for the table which is going to get dropped. | ||
| * | ||
| * @param tableName tableName. | ||
| */ | ||
| public void removeTableRegions(TableName tableName) { | ||
|
||
| regionSizes.entrySet().removeIf(regionInfo -> regionInfo.getKey().getTable().equals(tableName)); | ||
|
||
| } | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -24,17 +24,22 @@ | |
| import org.apache.hadoop.hbase.TableName; | ||
| import org.apache.hadoop.hbase.client.Admin; | ||
| import org.apache.hadoop.hbase.client.Connection; | ||
| import org.apache.hadoop.hbase.coprocessor.CoprocessorException; | ||
| import org.apache.hadoop.hbase.coprocessor.CoreCoprocessor; | ||
| import org.apache.hadoop.hbase.coprocessor.HasMasterServices; | ||
| import org.apache.hadoop.hbase.coprocessor.MasterCoprocessor; | ||
| import org.apache.hadoop.hbase.coprocessor.MasterCoprocessorEnvironment; | ||
| import org.apache.hadoop.hbase.coprocessor.MasterObserver; | ||
| import org.apache.hadoop.hbase.coprocessor.ObserverContext; | ||
| import org.apache.hadoop.hbase.master.MasterServices; | ||
| import org.apache.yetus.audience.InterfaceAudience; | ||
| import org.apache.hadoop.hbase.shaded.protobuf.generated.QuotaProtos.Quotas; | ||
|
|
||
| /** | ||
| * An observer to automatically delete quotas when a table/namespace | ||
| * is deleted. | ||
| */ | ||
| @CoreCoprocessor | ||
| @InterfaceAudience.Private | ||
| public class MasterQuotasObserver implements MasterCoprocessor, MasterObserver { | ||
| 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 { | |
| private CoprocessorEnvironment cpEnv; | ||
| private Configuration conf; | ||
| private boolean quotasEnabled = false; | ||
| private MasterServices masterServices; | ||
|
|
||
| @Override | ||
| public Optional<MasterObserver> getMasterObserver() { | ||
|
|
@@ -51,9 +57,19 @@ public Optional<MasterObserver> getMasterObserver() { | |
|
|
||
| @Override | ||
| public void start(CoprocessorEnvironment ctx) throws IOException { | ||
| this.cpEnv = ctx; | ||
| this.conf = cpEnv.getConfiguration(); | ||
| this.conf = ctx.getConfiguration(); | ||
| this.quotasEnabled = QuotaUtil.isQuotaEnabled(conf); | ||
|
|
||
| if (!(ctx instanceof MasterCoprocessorEnvironment)) { | ||
| throw new CoprocessorException("Must be loaded on master."); | ||
| } | ||
| // if running on master | ||
| MasterCoprocessorEnvironment mEnv = (MasterCoprocessorEnvironment) ctx; | ||
| if (mEnv instanceof HasMasterServices) { | ||
| this.masterServices = ((HasMasterServices) mEnv).getMasterServices(); | ||
| } else { | ||
| throw new CoprocessorException("Must be loaded on a master having master services."); | ||
| } | ||
| } | ||
|
|
||
| @Override | ||
|
|
@@ -65,8 +81,11 @@ public void postDeleteTable( | |
| } | ||
| final Connection conn = ctx.getEnvironment().getConnection(); | ||
| Quotas quotas = QuotaUtil.getTableQuota(conn, tableName); | ||
| Quotas quotasAtNamespace = QuotaUtil.getNamespaceQuota(conn, tableName.getNamespaceAsString()); | ||
|
||
| if (quotas != null){ | ||
|
||
| if (quotas.hasSpace()){ | ||
| // Remove regions of table from space quota map. | ||
| this.masterServices.getMasterQuotaManager().removeTableRegions(tableName); | ||
| QuotaSettings settings = QuotaSettingsFactory.removeTableSpaceLimit(tableName); | ||
| try (Admin admin = conn.getAdmin()) { | ||
| admin.setQuota(settings); | ||
|
|
@@ -78,6 +97,13 @@ public void postDeleteTable( | |
| admin.setQuota(settings); | ||
| } | ||
| } | ||
| } else { | ||
| if (quotasAtNamespace != null) { | ||
| if (quotasAtNamespace.hasSpace()) { | ||
| // Remove regions of table from space quota map. | ||
| this.masterServices.getMasterQuotaManager().removeTableRegions(tableName); | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -15,16 +15,23 @@ | |
| */ | ||
| package org.apache.hadoop.hbase.quotas; | ||
|
|
||
| import java.util.List; | ||
| import java.util.Map; | ||
| import java.util.concurrent.atomic.AtomicLong; | ||
|
|
||
| import org.apache.hadoop.conf.Configuration; | ||
| import org.apache.hadoop.hbase.HBaseClassTestRule; | ||
| import org.apache.hadoop.hbase.HBaseTestingUtility; | ||
| import org.apache.hadoop.hbase.MetaTableAccessor; | ||
| import org.apache.hadoop.hbase.TableName; | ||
| import org.apache.hadoop.hbase.Waiter; | ||
| import org.apache.hadoop.hbase.client.Put; | ||
| import org.apache.hadoop.hbase.client.RegionInfo; | ||
| import org.apache.hadoop.hbase.master.HMaster; | ||
| import org.apache.hadoop.hbase.testclassification.LargeTests; | ||
| import org.apache.hadoop.hbase.util.Bytes; | ||
| import org.junit.AfterClass; | ||
| import org.junit.Assert; | ||
| import org.junit.Before; | ||
| import org.junit.BeforeClass; | ||
| import org.junit.ClassRule; | ||
|
|
@@ -87,6 +94,50 @@ public void testSetQuotaAndThenDropTableWithDisable() throws Exception { | |
| setQuotaAndThenDropTable(SpaceViolationPolicy.DISABLE); | ||
| } | ||
|
|
||
| @Test | ||
| public void testSetQuotaAndThenDropTableWithRegionReport() throws Exception { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Great job at capturing this in a concise test case.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for the review @joshelser . Did all the changes and pushed. :) |
||
| final TableName tn = helper.createTable(); | ||
| helper.setQuotaLimit(tn, SpaceViolationPolicy.NO_INSERTS, 1L); | ||
| helper.writeData(tn, 2L); | ||
|
|
||
| final HMaster master = TEST_UTIL.getMiniHBaseCluster().getMaster(); | ||
| final MasterQuotaManager quotaManager = master.getMasterQuotaManager(); | ||
|
|
||
| // Make sure the master has report for the table. | ||
| Waiter.waitFor(TEST_UTIL.getConfiguration(), 30 * 1000, new Waiter.Predicate<Exception>() { | ||
| @Override | ||
| public boolean evaluate() throws Exception { | ||
| Map<RegionInfo, Long> regionSizes = quotaManager.snapshotRegionSizes(); | ||
| List<RegionInfo> tableRegions = | ||
| MetaTableAccessor.getTableRegions(TEST_UTIL.getConnection(), tn); | ||
| return regionSizes.containsKey(tableRegions.get(0)); | ||
| } | ||
| }); | ||
|
|
||
| boolean beforeDrop = false; | ||
|
||
|
|
||
| // region report should be present before dropping the table. | ||
| for (Map.Entry<RegionInfo, Long> entry : quotaManager.snapshotRegionSizes().entrySet()) { | ||
| if (entry.getKey().getTable().equals(tn)) { | ||
| beforeDrop = true; | ||
| break; | ||
| } | ||
| } | ||
|
|
||
| Assert.assertTrue(beforeDrop); | ||
|
||
|
|
||
| // drop the table | ||
| TEST_UTIL.getAdmin().disableTable(tn); | ||
| TEST_UTIL.getAdmin().deleteTable(tn); | ||
|
|
||
| // check if deleted table region report still present in the map. | ||
| for (Map.Entry<RegionInfo, Long> entry : quotaManager.snapshotRegionSizes().entrySet()) { | ||
| if (entry.getKey().getTable().equals(tn)) { | ||
| Assert.fail("Not deleted during the drop command"); | ||
|
||
| } | ||
| } | ||
| } | ||
|
|
||
| private void setQuotaAndThenDropTable(SpaceViolationPolicy policy) throws Exception { | ||
| Put put = new Put(Bytes.toBytes("to_reject")); | ||
| put.addColumn(Bytes.toBytes(SpaceQuotaHelperForTests.F1), Bytes.toBytes("to"), | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you change this to: "Removes each region size entry where the RegionInfo references the provided TableName". A little more clear about what this method does (instead of the context in which you are calling it).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done