Skip to content

Commit b389c7b

Browse files
shardul-cr7Jenkins
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). Closes apache#572 Signed-off-by: Josh Elser <[email protected]> (cherry picked from commit 16d6a9f) Change-Id: I01cbb39ef7046fedf2feff558b9f414b6397a19e
1 parent 5bb8287 commit b389c7b

File tree

4 files changed

+91
-9
lines changed

4 files changed

+91
-9
lines changed

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

Lines changed: 49 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;
@@ -693,23 +695,66 @@ public Map<RegionInfo, Long> snapshotRegionSizes() {
693695
return copy;
694696
}
695697

696-
int pruneEntriesOlderThan(long timeToPruneBefore) {
698+
int pruneEntriesOlderThan(long timeToPruneBefore, QuotaObserverChore quotaObserverChore) {
697699
if (regionSizes == null) {
698700
return 0;
699701
}
700702
int numEntriesRemoved = 0;
701-
Iterator<Entry<RegionInfo,SizeSnapshotWithTimestamp>> iterator =
703+
Iterator<Entry<RegionInfo, SizeSnapshotWithTimestamp>> iterator =
702704
regionSizes.entrySet().iterator();
703705
while (iterator.hasNext()) {
704-
long currentEntryTime = iterator.next().getValue().getTime();
705-
if (currentEntryTime < timeToPruneBefore) {
706+
RegionInfo regionInfo = iterator.next().getKey();
707+
long currentEntryTime = regionSizes.get(regionInfo).getTime();
708+
// do not prune the entries if table is in violation and
709+
// violation policy is disable to avoid cycle of enable/disable.
710+
// Please refer HBASE-22012 for more details.
711+
// prune entries older than time.
712+
if (currentEntryTime < timeToPruneBefore && !isInViolationAndPolicyDisable(
713+
regionInfo.getTable(), quotaObserverChore)) {
706714
iterator.remove();
707715
numEntriesRemoved++;
708716
}
709717
}
710718
return numEntriesRemoved;
711719
}
712720

721+
/**
722+
* Method to check if a table is in violation and policy set on table is DISABLE.
723+
*
724+
* @param tableName tableName to check.
725+
* @param quotaObserverChore QuotaObserverChore instance
726+
* @return returns true if table is in violation and policy is disable else false.
727+
*/
728+
private boolean isInViolationAndPolicyDisable(TableName tableName,
729+
QuotaObserverChore quotaObserverChore) {
730+
boolean isInViolationAtTable = false;
731+
boolean isInViolationAtNamespace = false;
732+
SpaceViolationPolicy tablePolicy = null;
733+
SpaceViolationPolicy namespacePolicy = null;
734+
// Get Current Snapshot for the given table
735+
SpaceQuotaSnapshot tableQuotaSnapshot = quotaObserverChore.getTableQuotaSnapshot(tableName);
736+
SpaceQuotaSnapshot namespaceQuotaSnapshot =
737+
quotaObserverChore.getNamespaceQuotaSnapshot(tableName.getNamespaceAsString());
738+
if (tableQuotaSnapshot != null) {
739+
// check if table in violation
740+
isInViolationAtTable = tableQuotaSnapshot.getQuotaStatus().isInViolation();
741+
Optional<SpaceViolationPolicy> policy = tableQuotaSnapshot.getQuotaStatus().getPolicy();
742+
if (policy.isPresent()) {
743+
tablePolicy = policy.get();
744+
}
745+
}
746+
if (namespaceQuotaSnapshot != null) {
747+
// check namespace in violation
748+
isInViolationAtNamespace = namespaceQuotaSnapshot.getQuotaStatus().isInViolation();
749+
Optional<SpaceViolationPolicy> policy = namespaceQuotaSnapshot.getQuotaStatus().getPolicy();
750+
if (policy.isPresent()) {
751+
namespacePolicy = policy.get();
752+
}
753+
}
754+
return (tablePolicy == SpaceViolationPolicy.DISABLE && isInViolationAtTable) || (
755+
namespacePolicy == SpaceViolationPolicy.DISABLE && isInViolationAtNamespace);
756+
}
757+
713758
/**
714759
* Removes each region size entry where the RegionInfo references the provided TableName.
715760
*

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)