001/** 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.hadoop.hbase.quotas; 019 020import static org.junit.Assert.assertEquals; 021import static org.junit.Assert.assertFalse; 022import static org.junit.Assert.assertTrue; 023 024import java.io.IOException; 025import java.util.ArrayList; 026import java.util.HashMap; 027import java.util.List; 028import java.util.Map; 029import java.util.concurrent.TimeUnit; 030import org.apache.hadoop.hbase.Cell; 031import org.apache.hadoop.hbase.CellScanner; 032import org.apache.hadoop.hbase.HBaseClassTestRule; 033import org.apache.hadoop.hbase.HBaseTestingUtility; 034import org.apache.hadoop.hbase.HConstants; 035import org.apache.hadoop.hbase.NamespaceDescriptor; 036import org.apache.hadoop.hbase.TableName; 037import org.apache.hadoop.hbase.client.Connection; 038import org.apache.hadoop.hbase.client.ConnectionFactory; 039import org.apache.hadoop.hbase.client.Put; 040import org.apache.hadoop.hbase.client.Result; 041import org.apache.hadoop.hbase.client.ResultScanner; 042import org.apache.hadoop.hbase.client.Table; 043import org.apache.hadoop.hbase.quotas.SpaceQuotaSnapshot.SpaceQuotaStatus; 044import org.apache.hadoop.hbase.testclassification.MasterTests; 045import org.apache.hadoop.hbase.testclassification.MediumTests; 046import org.junit.After; 047import org.junit.AfterClass; 048import org.junit.Before; 049import org.junit.BeforeClass; 050import org.junit.ClassRule; 051import org.junit.Rule; 052import org.junit.Test; 053import org.junit.experimental.categories.Category; 054import org.junit.rules.TestName; 055 056import org.apache.hbase.thirdparty.com.google.protobuf.UnsafeByteOperations; 057 058import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil; 059import org.apache.hadoop.hbase.shaded.protobuf.generated.QuotaProtos; 060import org.apache.hadoop.hbase.shaded.protobuf.generated.QuotaProtos.Quotas; 061import org.apache.hadoop.hbase.shaded.protobuf.generated.QuotaProtos.Throttle; 062 063/** 064 * Test the quota table helpers (e.g. CRUD operations) 065 */ 066@Category({MasterTests.class, MediumTests.class}) 067public class TestQuotaTableUtil { 068 069 @ClassRule 070 public static final HBaseClassTestRule CLASS_RULE = 071 HBaseClassTestRule.forClass(TestQuotaTableUtil.class); 072 073 private final static HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility(); 074 private Connection connection; 075 private int tableNameCounter; 076 077 @Rule 078 public TestName testName = new TestName(); 079 080 @Rule 081 public TestName name = new TestName(); 082 083 @BeforeClass 084 public static void setUpBeforeClass() throws Exception { 085 TEST_UTIL.getConfiguration().setBoolean(QuotaUtil.QUOTA_CONF_KEY, true); 086 TEST_UTIL.getConfiguration().setInt(QuotaCache.REFRESH_CONF_KEY, 2000); 087 TEST_UTIL.getConfiguration().setInt("hbase.hstore.compactionThreshold", 10); 088 TEST_UTIL.getConfiguration().setInt("hbase.regionserver.msginterval", 100); 089 TEST_UTIL.getConfiguration().setInt("hbase.client.pause", 250); 090 TEST_UTIL.getConfiguration().setInt(HConstants.HBASE_CLIENT_RETRIES_NUMBER, 6); 091 TEST_UTIL.getConfiguration().setBoolean("hbase.master.enabletable.roundrobin", true); 092 TEST_UTIL.startMiniCluster(1); 093 TEST_UTIL.waitTableAvailable(QuotaTableUtil.QUOTA_TABLE_NAME); 094 } 095 096 @AfterClass 097 public static void tearDownAfterClass() throws Exception { 098 TEST_UTIL.shutdownMiniCluster(); 099 } 100 101 @Before 102 public void before() throws IOException { 103 this.connection = ConnectionFactory.createConnection(TEST_UTIL.getConfiguration()); 104 this.tableNameCounter = 0; 105 } 106 107 @After 108 public void after() throws IOException { 109 this.connection.close(); 110 } 111 112 @Test 113 public void testTableQuotaUtil() throws Exception { 114 final TableName tableName = TableName.valueOf(name.getMethodName()); 115 116 Quotas quota = Quotas.newBuilder() 117 .setThrottle(Throttle.newBuilder() 118 .setReqNum(ProtobufUtil.toTimedQuota(1000, TimeUnit.SECONDS, QuotaScope.MACHINE)) 119 .setWriteNum(ProtobufUtil.toTimedQuota(600, TimeUnit.SECONDS, QuotaScope.MACHINE)) 120 .setReadSize(ProtobufUtil.toTimedQuota(8192, TimeUnit.SECONDS, QuotaScope.MACHINE)) 121 .build()) 122 .build(); 123 124 // Add user quota and verify it 125 QuotaUtil.addTableQuota(this.connection, tableName, quota); 126 Quotas resQuota = QuotaUtil.getTableQuota(this.connection, tableName); 127 assertEquals(quota, resQuota); 128 129 // Remove user quota and verify it 130 QuotaUtil.deleteTableQuota(this.connection, tableName); 131 resQuota = QuotaUtil.getTableQuota(this.connection, tableName); 132 assertEquals(null, resQuota); 133 } 134 135 @Test 136 public void testNamespaceQuotaUtil() throws Exception { 137 final String namespace = "testNamespaceQuotaUtilNS"; 138 139 Quotas quota = Quotas.newBuilder() 140 .setThrottle(Throttle.newBuilder() 141 .setReqNum(ProtobufUtil.toTimedQuota(1000, TimeUnit.SECONDS, QuotaScope.MACHINE)) 142 .setWriteNum(ProtobufUtil.toTimedQuota(600, TimeUnit.SECONDS, QuotaScope.MACHINE)) 143 .setReadSize(ProtobufUtil.toTimedQuota(8192, TimeUnit.SECONDS, QuotaScope.MACHINE)) 144 .build()) 145 .build(); 146 147 // Add user quota and verify it 148 QuotaUtil.addNamespaceQuota(this.connection, namespace, quota); 149 Quotas resQuota = QuotaUtil.getNamespaceQuota(this.connection, namespace); 150 assertEquals(quota, resQuota); 151 152 // Remove user quota and verify it 153 QuotaUtil.deleteNamespaceQuota(this.connection, namespace); 154 resQuota = QuotaUtil.getNamespaceQuota(this.connection, namespace); 155 assertEquals(null, resQuota); 156 } 157 158 @Test 159 public void testUserQuotaUtil() throws Exception { 160 final TableName tableName = TableName.valueOf(name.getMethodName()); 161 final String namespace = "testNS"; 162 final String user = "testUser"; 163 164 Quotas quotaNamespace = Quotas.newBuilder() 165 .setThrottle(Throttle.newBuilder() 166 .setReqNum(ProtobufUtil.toTimedQuota(50000, TimeUnit.SECONDS, QuotaScope.MACHINE)) 167 .build()) 168 .build(); 169 Quotas quotaTable = Quotas.newBuilder() 170 .setThrottle(Throttle.newBuilder() 171 .setReqNum(ProtobufUtil.toTimedQuota(1000, TimeUnit.SECONDS, QuotaScope.MACHINE)) 172 .setWriteNum(ProtobufUtil.toTimedQuota(600, TimeUnit.SECONDS, QuotaScope.MACHINE)) 173 .setReadSize(ProtobufUtil.toTimedQuota(10000, TimeUnit.SECONDS, QuotaScope.MACHINE)) 174 .build()) 175 .build(); 176 Quotas quota = Quotas.newBuilder() 177 .setThrottle(Throttle.newBuilder() 178 .setReqSize(ProtobufUtil.toTimedQuota(8192, TimeUnit.SECONDS, QuotaScope.MACHINE)) 179 .setWriteSize(ProtobufUtil.toTimedQuota(4096, TimeUnit.SECONDS, QuotaScope.MACHINE)) 180 .setReadNum(ProtobufUtil.toTimedQuota(1000, TimeUnit.SECONDS, QuotaScope.MACHINE)) 181 .build()) 182 .build(); 183 184 // Add user global quota 185 QuotaUtil.addUserQuota(this.connection, user, quota); 186 Quotas resQuota = QuotaUtil.getUserQuota(this.connection, user); 187 assertEquals(quota, resQuota); 188 189 // Add user quota for table 190 QuotaUtil.addUserQuota(this.connection, user, tableName, quotaTable); 191 Quotas resQuotaTable = QuotaUtil.getUserQuota(this.connection, user, tableName); 192 assertEquals(quotaTable, resQuotaTable); 193 194 // Add user quota for namespace 195 QuotaUtil.addUserQuota(this.connection, user, namespace, quotaNamespace); 196 Quotas resQuotaNS = QuotaUtil.getUserQuota(this.connection, user, namespace); 197 assertEquals(quotaNamespace, resQuotaNS); 198 199 // Delete user global quota 200 QuotaUtil.deleteUserQuota(this.connection, user); 201 resQuota = QuotaUtil.getUserQuota(this.connection, user); 202 assertEquals(null, resQuota); 203 204 // Delete user quota for table 205 QuotaUtil.deleteUserQuota(this.connection, user, tableName); 206 resQuotaTable = QuotaUtil.getUserQuota(this.connection, user, tableName); 207 assertEquals(null, resQuotaTable); 208 209 // Delete user quota for namespace 210 QuotaUtil.deleteUserQuota(this.connection, user, namespace); 211 resQuotaNS = QuotaUtil.getUserQuota(this.connection, user, namespace); 212 assertEquals(null, resQuotaNS); 213 } 214 215 @Test 216 public void testSerDeViolationPolicies() throws Exception { 217 final TableName tn1 = getUniqueTableName(); 218 final SpaceQuotaSnapshot snapshot1 = new SpaceQuotaSnapshot( 219 new SpaceQuotaStatus(SpaceViolationPolicy.DISABLE), 512L, 1024L); 220 final TableName tn2 = getUniqueTableName(); 221 final SpaceQuotaSnapshot snapshot2 = new SpaceQuotaSnapshot( 222 new SpaceQuotaStatus(SpaceViolationPolicy.NO_INSERTS), 512L, 1024L); 223 final TableName tn3 = getUniqueTableName(); 224 final SpaceQuotaSnapshot snapshot3 = new SpaceQuotaSnapshot( 225 new SpaceQuotaStatus(SpaceViolationPolicy.NO_WRITES), 512L, 1024L); 226 List<Put> puts = new ArrayList<>(); 227 puts.add(QuotaTableUtil.createPutForSpaceSnapshot(tn1, snapshot1)); 228 puts.add(QuotaTableUtil.createPutForSpaceSnapshot(tn2, snapshot2)); 229 puts.add(QuotaTableUtil.createPutForSpaceSnapshot(tn3, snapshot3)); 230 final Map<TableName,SpaceQuotaSnapshot> expectedPolicies = new HashMap<>(); 231 expectedPolicies.put(tn1, snapshot1); 232 expectedPolicies.put(tn2, snapshot2); 233 expectedPolicies.put(tn3, snapshot3); 234 235 final Map<TableName,SpaceQuotaSnapshot> actualPolicies = new HashMap<>(); 236 try (Table quotaTable = connection.getTable(QuotaUtil.QUOTA_TABLE_NAME)) { 237 quotaTable.put(puts); 238 ResultScanner scanner = quotaTable.getScanner(QuotaTableUtil.makeQuotaSnapshotScan()); 239 for (Result r : scanner) { 240 QuotaTableUtil.extractQuotaSnapshot(r, actualPolicies); 241 } 242 scanner.close(); 243 } 244 245 assertEquals(expectedPolicies, actualPolicies); 246 } 247 248 @Test 249 public void testSerdeTableSnapshotSizes() throws Exception { 250 TableName tn1 = TableName.valueOf("tn1"); 251 TableName tn2 = TableName.valueOf("tn2"); 252 try (Table quotaTable = connection.getTable(QuotaTableUtil.QUOTA_TABLE_NAME)) { 253 for (int i = 0; i < 3; i++) { 254 Put p = QuotaTableUtil.createPutForSnapshotSize(tn1, "tn1snap" + i, 1024L * (1+i)); 255 quotaTable.put(p); 256 } 257 for (int i = 0; i < 3; i++) { 258 Put p = QuotaTableUtil.createPutForSnapshotSize(tn2, "tn2snap" + i, 2048L * (1+i)); 259 quotaTable.put(p); 260 } 261 262 verifyTableSnapshotSize(quotaTable, tn1, "tn1snap0", 1024L); 263 verifyTableSnapshotSize(quotaTable, tn1, "tn1snap1", 2048L); 264 verifyTableSnapshotSize(quotaTable, tn1, "tn1snap2", 3072L); 265 266 verifyTableSnapshotSize(quotaTable, tn2, "tn2snap0", 2048L); 267 verifyTableSnapshotSize(quotaTable, tn2, "tn2snap1", 4096L); 268 verifyTableSnapshotSize(quotaTable, tn2, "tn2snap2", 6144L); 269 } 270 } 271 272 @Test 273 public void testReadNamespaceSnapshotSizes() throws Exception { 274 String ns1 = "ns1"; 275 String ns2 = "ns2"; 276 String defaultNs = NamespaceDescriptor.DEFAULT_NAMESPACE_NAME_STR; 277 try (Table quotaTable = connection.getTable(QuotaTableUtil.QUOTA_TABLE_NAME)) { 278 quotaTable.put(QuotaTableUtil.createPutForNamespaceSnapshotSize(ns1, 1024L)); 279 quotaTable.put(QuotaTableUtil.createPutForNamespaceSnapshotSize(ns2, 2048L)); 280 quotaTable.put(QuotaTableUtil.createPutForNamespaceSnapshotSize(defaultNs, 8192L)); 281 282 assertEquals(1024L, QuotaTableUtil.getNamespaceSnapshotSize(connection, ns1)); 283 assertEquals(2048L, QuotaTableUtil.getNamespaceSnapshotSize(connection, ns2)); 284 assertEquals(8192L, QuotaTableUtil.getNamespaceSnapshotSize(connection, defaultNs)); 285 } 286 } 287 288 private TableName getUniqueTableName() { 289 return TableName.valueOf(testName.getMethodName() + "_" + tableNameCounter++); 290 } 291 292 private void verifyTableSnapshotSize( 293 Table quotaTable, TableName tn, String snapshotName, long expectedSize) throws IOException { 294 Result r = quotaTable.get(QuotaTableUtil.makeGetForSnapshotSize(tn, snapshotName)); 295 CellScanner cs = r.cellScanner(); 296 assertTrue(cs.advance()); 297 Cell c = cs.current(); 298 assertEquals(expectedSize, QuotaProtos.SpaceQuotaSnapshot.parseFrom( 299 UnsafeByteOperations.unsafeWrap( 300 c.getValueArray(), c.getValueOffset(), c.getValueLength())).getQuotaUsage()); 301 assertFalse(cs.advance()); 302 } 303}