Skip to content

Commit 23adf23

Browse files
shardul-cr7joshelser
authored andcommitted
HBASE-22012 Prevent DISABLE quota violation policy from disabling violation policy
Space quotas has a feature which intends to avoid enacting a space quota violation policy when only a subset of the Regions for that Table have reported their space usage (under the assumption that we cannot make an informed decision if we do not include all regions in our calculations). This had the unintended side-effect, when a table is disabled as a part of a violation policy, of causing the regions for that table to not be reported which disables the violation policy and enables the table. Need to make sure that when a table is disabled because of a violation policy that the code does not automatically move that table out of violation because region sizes are not being reported (because those regions are not open). elserj: Had to replace some usages of Optional that don't exist in branch-2.1 Closes #572 Signed-off-by: Josh Elser <[email protected]>
1 parent 7ac1f27 commit 23adf23

File tree

4 files changed

+89
-9
lines changed

4 files changed

+89
-9
lines changed

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

Lines changed: 47 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
import java.util.Iterator;
2626
import java.util.Map;
2727
import java.util.Map.Entry;
28+
import java.util.Optional;
29+
import java.util.Set;
2830
import java.util.concurrent.ConcurrentHashMap;
2931

3032
import org.apache.commons.lang3.builder.HashCodeBuilder;
@@ -560,23 +562,64 @@ public Map<RegionInfo, Long> snapshotRegionSizes() {
560562
return copy;
561563
}
562564

563-
int pruneEntriesOlderThan(long timeToPruneBefore) {
565+
int pruneEntriesOlderThan(long timeToPruneBefore, QuotaObserverChore quotaObserverChore) {
564566
if (regionSizes == null) {
565567
return 0;
566568
}
567569
int numEntriesRemoved = 0;
568-
Iterator<Entry<RegionInfo,SizeSnapshotWithTimestamp>> iterator =
570+
Iterator<Entry<RegionInfo, SizeSnapshotWithTimestamp>> iterator =
569571
regionSizes.entrySet().iterator();
570572
while (iterator.hasNext()) {
571-
long currentEntryTime = iterator.next().getValue().getTime();
572-
if (currentEntryTime < timeToPruneBefore) {
573+
RegionInfo regionInfo = iterator.next().getKey();
574+
long currentEntryTime = regionSizes.get(regionInfo).getTime();
575+
// do not prune the entries if table is in violation and
576+
// violation policy is disable to avoid cycle of enable/disable.
577+
// Please refer HBASE-22012 for more details.
578+
// prune entries older than time.
579+
if (currentEntryTime < timeToPruneBefore && !isInViolationAndPolicyDisable(
580+
regionInfo.getTable(), quotaObserverChore)) {
573581
iterator.remove();
574582
numEntriesRemoved++;
575583
}
576584
}
577585
return numEntriesRemoved;
578586
}
579587

588+
/**
589+
* Method to check if a table is in violation and policy set on table is DISABLE.
590+
*
591+
* @param tableName tableName to check.
592+
* @param quotaObserverChore QuotaObserverChore instance
593+
* @return returns true if table is in violation and policy is disable else false.
594+
*/
595+
private boolean isInViolationAndPolicyDisable(TableName tableName,
596+
QuotaObserverChore quotaObserverChore) {
597+
boolean isInViolationAtTable = false;
598+
boolean isInViolationAtNamespace = false;
599+
SpaceViolationPolicy tablePolicy = null;
600+
SpaceViolationPolicy namespacePolicy = null;
601+
// Get Current Snapshot for the given table
602+
SpaceQuotaSnapshot tableQuotaSnapshot = quotaObserverChore.getTableQuotaSnapshot(tableName);
603+
SpaceQuotaSnapshot namespaceQuotaSnapshot =
604+
quotaObserverChore.getNamespaceQuotaSnapshot(tableName.getNamespaceAsString());
605+
if (tableQuotaSnapshot != null) {
606+
// check if table in violation
607+
isInViolationAtTable = tableQuotaSnapshot.getQuotaStatus().isInViolation();
608+
if (isInViolationAtTable) {
609+
tablePolicy = tableQuotaSnapshot.getQuotaStatus().getPolicy();
610+
}
611+
}
612+
if (namespaceQuotaSnapshot != null) {
613+
// check namespace in violation
614+
isInViolationAtNamespace = namespaceQuotaSnapshot.getQuotaStatus().isInViolation();
615+
if (isInViolationAtNamespace) {
616+
namespacePolicy = namespaceQuotaSnapshot.getQuotaStatus().getPolicy();
617+
}
618+
}
619+
return (tablePolicy == SpaceViolationPolicy.DISABLE && isInViolationAtTable) || (
620+
namespacePolicy == SpaceViolationPolicy.DISABLE && isInViolationAtNamespace);
621+
}
622+
580623
/**
581624
* Removes each region size entry where the RegionInfo references the provided TableName.
582625
*

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -472,7 +472,7 @@ void updateNamespaceQuota(
472472
void pruneOldRegionReports() {
473473
final long now = EnvironmentEdgeManager.currentTime();
474474
final long pruneTime = now - regionReportLifetimeMillis;
475-
final int numRemoved = quotaManager.pruneEntriesOlderThan(pruneTime);
475+
final int numRemoved = quotaManager.pruneEntriesOlderThan(pruneTime,this);
476476
if (LOG.isTraceEnabled()) {
477477
LOG.trace("Removed " + numRemoved + " old region size reports that were older than "
478478
+ pruneTime + ".");

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

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -73,18 +73,19 @@ public void testOldEntriesRemoved() {
7373

7474
assertEquals(5, manager.snapshotRegionSizes().size());
7575

76+
QuotaObserverChore chore = mock(QuotaObserverChore.class);
7677
// Prune nothing
77-
assertEquals(0, manager.pruneEntriesOlderThan(0));
78+
assertEquals(0, manager.pruneEntriesOlderThan(0, chore));
7879
assertEquals(5, manager.snapshotRegionSizes().size());
79-
assertEquals(0, manager.pruneEntriesOlderThan(10));
80+
assertEquals(0, manager.pruneEntriesOlderThan(10, chore));
8081
assertEquals(5, manager.snapshotRegionSizes().size());
8182

8283
// Prune the elements at time1
83-
assertEquals(2, manager.pruneEntriesOlderThan(15));
84+
assertEquals(2, manager.pruneEntriesOlderThan(15, chore));
8485
assertEquals(3, manager.snapshotRegionSizes().size());
8586

8687
// Prune the elements at time2
87-
assertEquals(2, manager.pruneEntriesOlderThan(30));
88+
assertEquals(2, manager.pruneEntriesOlderThan(30, chore));
8889
assertEquals(1, manager.snapshotRegionSizes().size());
8990
}
9091
}

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

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,24 +19,31 @@
1919
import static org.junit.Assert.assertTrue;
2020
import static org.junit.Assert.fail;
2121

22+
import java.util.List;
23+
import java.util.Map;
2224
import java.util.concurrent.atomic.AtomicLong;
2325

2426
import org.apache.hadoop.conf.Configuration;
2527
import org.apache.hadoop.hbase.DoNotRetryIOException;
2628
import org.apache.hadoop.hbase.HBaseClassTestRule;
2729
import org.apache.hadoop.hbase.HBaseTestingUtility;
30+
import org.apache.hadoop.hbase.MetaTableAccessor;
2831
import org.apache.hadoop.hbase.TableName;
32+
import org.apache.hadoop.hbase.Waiter;
2933
import org.apache.hadoop.hbase.client.Admin;
3034
import org.apache.hadoop.hbase.client.Append;
3135
import org.apache.hadoop.hbase.client.Delete;
3236
import org.apache.hadoop.hbase.client.Increment;
3337
import org.apache.hadoop.hbase.client.Put;
38+
import org.apache.hadoop.hbase.client.RegionInfo;
3439
import org.apache.hadoop.hbase.client.Table;
40+
import org.apache.hadoop.hbase.master.HMaster;
3541
import org.apache.hadoop.hbase.security.AccessDeniedException;
3642
import org.apache.hadoop.hbase.testclassification.LargeTests;
3743
import org.apache.hadoop.hbase.util.Bytes;
3844
import org.apache.hadoop.util.StringUtils;
3945
import org.junit.AfterClass;
46+
import org.junit.Assert;
4047
import org.junit.Before;
4148
import org.junit.BeforeClass;
4249
import org.junit.ClassRule;
@@ -221,4 +228,33 @@ public void testTableQuotaOverridesNamespaceQuota() throws Exception {
221228
Bytes.toBytes("reject"));
222229
helper.verifyViolation(policy, tn, p);
223230
}
231+
232+
@Test
233+
public void testDisablePolicyQuotaAndViolate() throws Exception {
234+
TableName tableName = helper.createTable();
235+
helper.setQuotaLimit(tableName, SpaceViolationPolicy.DISABLE, 1L);
236+
helper.writeData(tableName, SpaceQuotaHelperForTests.ONE_MEGABYTE * 2L);
237+
TEST_UTIL.getConfiguration()
238+
.setLong("hbase.master.quotas.region.report.retention.millis", 100);
239+
240+
HMaster master = TEST_UTIL.getMiniHBaseCluster().getMaster();
241+
MasterQuotaManager quotaManager = master.getMasterQuotaManager();
242+
243+
// Make sure the master has report for the table.
244+
Waiter.waitFor(TEST_UTIL.getConfiguration(), 30 * 1000, new Waiter.Predicate<Exception>() {
245+
@Override
246+
public boolean evaluate() throws Exception {
247+
Map<RegionInfo, Long> regionSizes = quotaManager.snapshotRegionSizes();
248+
List<RegionInfo> tableRegions =
249+
MetaTableAccessor.getTableRegions(TEST_UTIL.getConnection(), tableName);
250+
return regionSizes.containsKey(tableRegions.get(0));
251+
}
252+
});
253+
254+
// Check if disabled table region report present in the map after retention period expired.
255+
// It should be present after retention period expired.
256+
final long regionSizes = quotaManager.snapshotRegionSizes().keySet().stream()
257+
.filter(k -> k.getTable().equals(tableName)).count();
258+
Assert.assertTrue(regionSizes > 0);
259+
}
224260
}

0 commit comments

Comments
 (0)