Skip to content

Commit 2c08876

Browse files
skochharjoshelser
authored andcommitted
HBASE-22146 Removing a namespace-level space quota does not remove policies against contained tables
Closes #1935 Signed-off-by: Josh Elser <elserj@apache.org>
1 parent 4e5ec22 commit 2c08876

4 files changed

Lines changed: 157 additions & 1 deletion

File tree

hbase-client/src/main/java/org/apache/hadoop/hbase/quotas/QuotaTableUtil.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -627,6 +627,34 @@ static List<Delete> createDeletesForExistingSnapshotsFromScan(Connection connect
627627
}
628628
}
629629

630+
/**
631+
* Remove table usage snapshots (u:p columns) for the namespace passed
632+
* @param connection connection to re-use
633+
* @param namespace the namespace to fetch the list of table usage snapshots
634+
*/
635+
static void deleteTableUsageSnapshotsForNamespace(Connection connection, String namespace)
636+
throws IOException {
637+
Scan s = new Scan();
638+
//Get rows for all tables in namespace
639+
s.setRowPrefixFilter(Bytes.add(QUOTA_TABLE_ROW_KEY_PREFIX, Bytes.toBytes(namespace + TableName.NAMESPACE_DELIM)));
640+
//Scan for table usage column (u:p) in quota table
641+
s.addColumn(QUOTA_FAMILY_USAGE,QUOTA_QUALIFIER_POLICY);
642+
//Scan for table quota column (q:s) if table has a space quota defined
643+
s.addColumn(QUOTA_FAMILY_INFO,QUOTA_QUALIFIER_SETTINGS);
644+
try (Table quotaTable = connection.getTable(QUOTA_TABLE_NAME);
645+
ResultScanner rs = quotaTable.getScanner(s)) {
646+
for (Result r : rs) {
647+
byte[] data = r.getValue(QUOTA_FAMILY_INFO, QUOTA_QUALIFIER_SETTINGS);
648+
//if table does not have a table space quota defined, delete table usage column (u:p)
649+
if (data == null) {
650+
Delete delete = new Delete(r.getRow());
651+
delete.addColumns(QUOTA_FAMILY_USAGE,QUOTA_QUALIFIER_POLICY);
652+
quotaTable.delete(delete);
653+
}
654+
}
655+
}
656+
}
657+
630658
/**
631659
* Fetches the computed size of all snapshots against tables in a namespace for space quotas.
632660
*/

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,14 @@ private static void deleteQuotas(final Connection connection, final byte[] rowKe
266266
if (qualifier != null) {
267267
delete.addColumns(QUOTA_FAMILY_INFO, qualifier);
268268
}
269+
if (isNamespaceRowKey(rowKey)) {
270+
String ns = getNamespaceFromRowKey(rowKey);
271+
Quotas namespaceQuota = getNamespaceQuota(connection,ns);
272+
if (namespaceQuota != null && namespaceQuota.hasSpace()) {
273+
// When deleting namespace space quota, also delete table usage(u:p) snapshots
274+
deleteTableUsageSnapshotsForNamespace(connection, ns);
275+
}
276+
}
269277
doDelete(connection, delete);
270278
}
271279

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

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,28 @@ TableName writeUntilViolation(SpaceViolationPolicy policyToViolate) throws Excep
161161
return tn;
162162
}
163163

164+
165+
TableName writeUntilViolationAndVerifyViolationInNamespace(
166+
String ns, SpaceViolationPolicy policyToViolate, Mutation m) throws Exception {
167+
final TableName tn = writeUntilViolationInNamespace(ns, policyToViolate);
168+
verifyViolation(policyToViolate, tn, m);
169+
return tn;
170+
}
171+
172+
TableName writeUntilViolationInNamespace(String ns, SpaceViolationPolicy policyToViolate) throws Exception {
173+
TableName tn = createTableWithRegions(ns,10);
174+
175+
setQuotaLimit(ns, policyToViolate, 4L);
176+
177+
// Write more data than should be allowed and flush it to disk
178+
writeData(tn, 5L * SpaceQuotaHelperForTests.ONE_MEGABYTE);
179+
180+
// This should be sufficient time for the chores to run and see the change.
181+
Thread.sleep(5000);
182+
183+
return tn;
184+
}
185+
164186
/**
165187
* Verifies that the given policy on the given table has been violated
166188
*/
@@ -263,6 +285,19 @@ void verifyNoViolation(TableName tn, Mutation m) throws Exception {
263285
assertTrue("Expected to succeed in writing data to a table not having quota ", sawSuccess);
264286
}
265287

288+
/**
289+
* Verifies that table usage snapshot exists for the table
290+
*/
291+
void verifyTableUsageSnapshotForSpaceQuotaExist(TableName tn) throws Exception {
292+
boolean sawUsageSnapshot = false;
293+
try (Table quotaTable = testUtil.getConnection().getTable(QuotaTableUtil.QUOTA_TABLE_NAME)) {
294+
Scan s = QuotaTableUtil.makeQuotaSnapshotScanForTable(tn);
295+
ResultScanner rs = quotaTable.getScanner(s);
296+
sawUsageSnapshot = (rs.next() != null);
297+
}
298+
assertTrue("Expected to succeed in getting table usage snapshots for space quota", sawUsageSnapshot);
299+
}
300+
266301
/**
267302
* Sets the given quota (policy & limit) on the passed table.
268303
*/
@@ -274,6 +309,17 @@ void setQuotaLimit(final TableName tn, SpaceViolationPolicy policy, long sizeInM
274309
LOG.debug("Quota limit set for table = {}, limit = {}", tn, sizeLimit);
275310
}
276311

312+
/**
313+
* Sets the given quota (policy & limit) on the passed namespace.
314+
*/
315+
void setQuotaLimit(String ns, SpaceViolationPolicy policy, long sizeInMBs)
316+
throws Exception {
317+
final long sizeLimit = sizeInMBs * SpaceQuotaHelperForTests.ONE_MEGABYTE;
318+
QuotaSettings settings = QuotaSettingsFactory.limitNamespaceSpace(ns, sizeLimit, policy);
319+
testUtil.getAdmin().setQuota(settings);
320+
LOG.debug("Quota limit set for namespace = {}, limit = {}", ns, sizeLimit);
321+
}
322+
277323
/**
278324
* Removes the space quota from the given table
279325
*/
@@ -363,6 +409,16 @@ public Void rpcCall() throws Exception {
363409
// };
364410
// }
365411

412+
/**
413+
* Removes the space quota from the given namespace
414+
*/
415+
void removeQuotaFromNamespace(String ns) throws Exception {
416+
QuotaSettings removeQuota = QuotaSettingsFactory.removeNamespaceSpaceLimit(ns);
417+
Admin admin = testUtil.getAdmin();
418+
admin.setQuota(removeQuota);
419+
LOG.debug("Space quota settings removed from the namespace ", ns);
420+
}
421+
366422
/**
367423
* Removes all quotas defined in the HBase quota table.
368424
*/
@@ -494,7 +550,13 @@ void writeData(TableName tn, long sizeInBytes, byte[] qual) throws IOException {
494550
}
495551

496552
NamespaceDescriptor createNamespace() throws Exception {
497-
NamespaceDescriptor nd = NamespaceDescriptor.create("ns" + counter.getAndIncrement()).build();
553+
return createNamespace(null);
554+
}
555+
556+
NamespaceDescriptor createNamespace(String namespace) throws Exception {
557+
if (namespace == null || namespace.trim().isEmpty())
558+
namespace = "ns" + counter.getAndIncrement();
559+
NamespaceDescriptor nd = NamespaceDescriptor.create(namespace).build();
498560
testUtil.getAdmin().createNamespace(nd);
499561
return nd;
500562
}

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

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import org.apache.hadoop.conf.Configuration;
2121
import org.apache.hadoop.hbase.HBaseClassTestRule;
2222
import org.apache.hadoop.hbase.HBaseTestingUtility;
23+
import org.apache.hadoop.hbase.NamespaceDescriptor;
2324
import org.apache.hadoop.hbase.TableName;
2425
import org.apache.hadoop.hbase.client.Put;
2526
import org.apache.hadoop.hbase.testclassification.LargeTests;
@@ -139,6 +140,63 @@ private void setQuotaAndThenRemove(SpaceViolationPolicy policy) throws Exception
139140
helper.verifyNoViolation(tn, put);
140141
}
141142

143+
@Test
144+
public void testDeleteTableUsageSnapshotsForNamespace() throws Exception {
145+
Put put = new Put(Bytes.toBytes("to_reject"));
146+
put.addColumn(Bytes.toBytes(SpaceQuotaHelperForTests.F1), Bytes.toBytes("to"),
147+
Bytes.toBytes("reject"));
148+
149+
SpaceViolationPolicy policy = SpaceViolationPolicy.NO_INSERTS;
150+
151+
//Create a namespace
152+
String ns1 = "nsnew";
153+
NamespaceDescriptor nsd = helper.createNamespace(ns1);
154+
155+
//Create 2nd namespace with name similar to ns1
156+
String ns2 = ns1 + "test";
157+
NamespaceDescriptor nsd2 = helper.createNamespace(ns2);
158+
159+
// Do puts until we violate space policy on table tn1 in namesapce ns1
160+
final TableName tn1 = helper.writeUntilViolationAndVerifyViolationInNamespace(ns1, policy, put);
161+
162+
// Do puts until we violate space policy on table tn2 in namespace ns2
163+
final TableName tn2 = helper.writeUntilViolationAndVerifyViolationInNamespace(ns2, policy, put);
164+
165+
// Now, remove the quota from namespace ns1 which will remove table usage snapshots for ns1
166+
helper.removeQuotaFromNamespace(ns1);
167+
168+
// Verify that table usage snapshot for table tn2 in namespace ns2 exist
169+
helper.verifyTableUsageSnapshotForSpaceQuotaExist(tn2);
170+
171+
// Put a new row on tn2: should violate as space quota exists on namespace ns2
172+
helper.verifyViolation(policy, tn2, put);
173+
174+
// Put a new row on tn1: should not violate as quota settings removed from namespace ns1
175+
helper.verifyNoViolation(tn1, put);
176+
}
177+
178+
@Test
179+
public void testSetNamespaceSizeQuotaAndThenRemove() throws Exception {
180+
Put put = new Put(Bytes.toBytes("to_reject"));
181+
put.addColumn(Bytes.toBytes(SpaceQuotaHelperForTests.F1), Bytes.toBytes("to"),
182+
Bytes.toBytes("reject"));
183+
184+
SpaceViolationPolicy policy = SpaceViolationPolicy.NO_INSERTS;
185+
186+
//Create namespace
187+
NamespaceDescriptor nsd = helper.createNamespace();
188+
String ns = nsd.getName();
189+
190+
// Do puts until we violate space policy on table tn1
191+
final TableName tn1 = helper.writeUntilViolationAndVerifyViolationInNamespace(ns, policy, put);
192+
193+
// Now, remove the quota from namespace
194+
helper.removeQuotaFromNamespace(ns);
195+
196+
// Put a new row now on tn1: should not violate as quota settings removed from namespace
197+
helper.verifyNoViolation(tn1, put);
198+
}
199+
142200
private void setQuotaAndThenRemoveInOneAmongTwoTables(SpaceViolationPolicy policy)
143201
throws Exception {
144202
Put put = new Put(Bytes.toBytes("to_reject"));

0 commit comments

Comments
 (0)