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.assertTrue;
022
023import java.io.IOException;
024import java.util.Set;
025import java.util.concurrent.TimeUnit;
026import java.util.concurrent.atomic.AtomicLong;
027import org.apache.hadoop.conf.Configuration;
028import org.apache.hadoop.hbase.HBaseClassTestRule;
029import org.apache.hadoop.hbase.HBaseTestingUtility;
030import org.apache.hadoop.hbase.HColumnDescriptor;
031import org.apache.hadoop.hbase.HTableDescriptor;
032import org.apache.hadoop.hbase.NamespaceDescriptor;
033import org.apache.hadoop.hbase.TableName;
034import org.apache.hadoop.hbase.client.Admin;
035import org.apache.hadoop.hbase.client.Connection;
036import org.apache.hadoop.hbase.master.HMaster;
037import org.apache.hadoop.hbase.master.MasterCoprocessorHost;
038import org.apache.hadoop.hbase.testclassification.MediumTests;
039import org.junit.AfterClass;
040import org.junit.Before;
041import org.junit.BeforeClass;
042import org.junit.ClassRule;
043import org.junit.Rule;
044import org.junit.Test;
045import org.junit.experimental.categories.Category;
046import org.junit.rules.TestName;
047
048/**
049 * Test class for {@link MasterQuotasObserver}.
050 */
051@Category(MediumTests.class)
052public class TestMasterQuotasObserver {
053
054  @ClassRule
055  public static final HBaseClassTestRule CLASS_RULE =
056      HBaseClassTestRule.forClass(TestMasterQuotasObserver.class);
057
058  private static final HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
059  private static SpaceQuotaHelperForTests helper;
060
061  @Rule
062  public TestName testName = new TestName();
063
064  @BeforeClass
065  public static void setUp() throws Exception {
066    Configuration conf = TEST_UTIL.getConfiguration();
067    conf.setBoolean(QuotaUtil.QUOTA_CONF_KEY, true);
068    TEST_UTIL.startMiniCluster(1);
069  }
070
071  @AfterClass
072  public static void tearDown() throws Exception {
073    TEST_UTIL.shutdownMiniCluster();
074  }
075
076  @Before
077  public void removeAllQuotas() throws Exception {
078    if (helper == null) {
079      helper = new SpaceQuotaHelperForTests(TEST_UTIL, testName, new AtomicLong());
080    }
081    final Connection conn = TEST_UTIL.getConnection();
082    // Wait for the quota table to be created
083    if (!conn.getAdmin().tableExists(QuotaUtil.QUOTA_TABLE_NAME)) {
084      helper.waitForQuotaTable(conn);
085    } else {
086      // Or, clean up any quotas from previous test runs.
087      helper.removeAllQuotas(conn);
088      assertEquals(0, helper.listNumDefinedQuotas(conn));
089    }
090  }
091
092  @Test
093  public void testTableSpaceQuotaRemoved() throws Exception {
094    final Connection conn = TEST_UTIL.getConnection();
095    final Admin admin = conn.getAdmin();
096    final TableName tn = TableName.valueOf(testName.getMethodName());
097    // Drop the table if it somehow exists
098    if (admin.tableExists(tn)) {
099      dropTable(admin, tn);
100    }
101    createTable(admin, tn);
102    assertEquals(0, getNumSpaceQuotas());
103
104    // Set space quota
105    QuotaSettings settings = QuotaSettingsFactory.limitTableSpace(
106        tn, 1024L, SpaceViolationPolicy.NO_INSERTS);
107    admin.setQuota(settings);
108    assertEquals(1, getNumSpaceQuotas());
109
110    // Drop the table and observe the Space quota being automatically deleted as well
111    dropTable(admin, tn);
112    assertEquals(0, getNumSpaceQuotas());
113  }
114
115  @Test
116  public void testTableRPCQuotaRemoved() throws Exception {
117    final Connection conn = TEST_UTIL.getConnection();
118    final Admin admin = conn.getAdmin();
119    final TableName tn = TableName.valueOf(testName.getMethodName());
120    // Drop the table if it somehow exists
121    if (admin.tableExists(tn)) {
122      dropTable(admin, tn);
123    }
124
125    createTable(admin, tn);
126    assertEquals(0, getThrottleQuotas());
127
128    // Set RPC quota
129    QuotaSettings settings =
130        QuotaSettingsFactory.throttleTable(tn, ThrottleType.REQUEST_SIZE, 2L, TimeUnit.HOURS);
131    admin.setQuota(settings);
132
133    assertEquals(1, getThrottleQuotas());
134
135    // Delete the table and observe the RPC quota being automatically deleted as well
136    dropTable(admin, tn);
137    assertEquals(0, getThrottleQuotas());
138  }
139
140  @Test
141  public void testTableSpaceAndRPCQuotaRemoved() throws Exception {
142    final Connection conn = TEST_UTIL.getConnection();
143    final Admin admin = conn.getAdmin();
144    final TableName tn = TableName.valueOf(testName.getMethodName());
145    // Drop the table if it somehow exists
146    if (admin.tableExists(tn)) {
147      dropTable(admin, tn);
148    }
149
150    createTable(admin, tn);
151    assertEquals(0, getNumSpaceQuotas());
152    assertEquals(0, getThrottleQuotas());
153
154    // Set Both quotas
155    QuotaSettings settings =
156        QuotaSettingsFactory.limitTableSpace(tn, 1024L, SpaceViolationPolicy.NO_INSERTS);
157    admin.setQuota(settings);
158
159    settings =
160        QuotaSettingsFactory.throttleTable(tn, ThrottleType.REQUEST_SIZE, 2L, TimeUnit.HOURS);
161    admin.setQuota(settings);
162
163    assertEquals(1, getNumSpaceQuotas());
164    assertEquals(1, getThrottleQuotas());
165
166    // Delete the table and observe the quotas being automatically deleted as well
167    dropTable(admin, tn);
168    assertEquals(0, getNumSpaceQuotas());
169    assertEquals(0, getThrottleQuotas());
170  }
171
172  @Test
173  public void testNamespaceSpaceQuotaRemoved() throws Exception {
174    final Connection conn = TEST_UTIL.getConnection();
175    final Admin admin = conn.getAdmin();
176    final String ns = testName.getMethodName();
177    // Drop the ns if it somehow exists
178    if (namespaceExists(ns)) {
179      admin.deleteNamespace(ns);
180    }
181
182    // Create the ns
183    NamespaceDescriptor desc = NamespaceDescriptor.create(ns).build();
184    admin.createNamespace(desc);
185    assertEquals(0, getNumSpaceQuotas());
186
187    // Set a quota
188    QuotaSettings settings = QuotaSettingsFactory.limitNamespaceSpace(
189        ns, 1024L, SpaceViolationPolicy.NO_INSERTS);
190    admin.setQuota(settings);
191    assertEquals(1, getNumSpaceQuotas());
192
193    // Delete the namespace and observe the quota being automatically deleted as well
194    admin.deleteNamespace(ns);
195    assertEquals(0, getNumSpaceQuotas());
196  }
197
198  @Test
199  public void testNamespaceRPCQuotaRemoved() throws Exception {
200    final Connection conn = TEST_UTIL.getConnection();
201    final Admin admin = conn.getAdmin();
202    final String ns = testName.getMethodName();
203    // Drop the ns if it somehow exists
204    if (namespaceExists(ns)) {
205      admin.deleteNamespace(ns);
206    }
207
208    // Create the ns
209    NamespaceDescriptor desc = NamespaceDescriptor.create(ns).build();
210    admin.createNamespace(desc);
211    assertEquals(0, getThrottleQuotas());
212
213    // Set a quota
214    QuotaSettings settings =
215        QuotaSettingsFactory.throttleNamespace(ns, ThrottleType.REQUEST_SIZE, 2L, TimeUnit.HOURS);
216    admin.setQuota(settings);
217    assertEquals(1, getThrottleQuotas());
218
219    // Delete the namespace and observe the quota being automatically deleted as well
220    admin.deleteNamespace(ns);
221    assertEquals(0, getThrottleQuotas());
222  }
223
224  @Test
225  public void testNamespaceSpaceAndRPCQuotaRemoved() throws Exception {
226    final Connection conn = TEST_UTIL.getConnection();
227    final Admin admin = conn.getAdmin();
228    final TableName tn = TableName.valueOf(testName.getMethodName());
229    final String ns = testName.getMethodName();
230    // Drop the ns if it somehow exists
231    if (namespaceExists(ns)) {
232      admin.deleteNamespace(ns);
233    }
234
235    // Create the ns
236    NamespaceDescriptor desc = NamespaceDescriptor.create(ns).build();
237    admin.createNamespace(desc);
238    assertEquals(0, getNumSpaceQuotas());
239    assertEquals(0, getThrottleQuotas());
240
241    // Set Both quotas
242    QuotaSettings settings =
243        QuotaSettingsFactory.limitNamespaceSpace(ns, 1024L, SpaceViolationPolicy.NO_INSERTS);
244    admin.setQuota(settings);
245
246    settings =
247        QuotaSettingsFactory.throttleNamespace(ns, ThrottleType.REQUEST_SIZE, 2L, TimeUnit.HOURS);
248    admin.setQuota(settings);
249
250    assertEquals(1, getNumSpaceQuotas());
251    assertEquals(1, getThrottleQuotas());
252
253    // Delete the namespace and observe the quotas being automatically deleted as well
254    admin.deleteNamespace(ns);
255    assertEquals(0, getNumSpaceQuotas());
256    assertEquals(0, getThrottleQuotas());
257  }
258
259  @Test
260  public void testObserverAddedByDefault() throws Exception {
261    final HMaster master = TEST_UTIL.getHBaseCluster().getMaster();
262    final MasterCoprocessorHost cpHost = master.getMasterCoprocessorHost();
263    Set<String> coprocessorNames = cpHost.getCoprocessors();
264    assertTrue(
265        "Did not find MasterQuotasObserver in list of CPs: " + coprocessorNames,
266        coprocessorNames.contains(MasterQuotasObserver.class.getSimpleName()));
267  }
268
269  public boolean namespaceExists(String ns) throws IOException {
270    NamespaceDescriptor[] descs = TEST_UTIL.getAdmin().listNamespaceDescriptors();
271    for (NamespaceDescriptor desc : descs) {
272      if (ns.equals(desc.getName())) {
273        return true;
274      }
275    }
276    return false;
277  }
278
279  public int getNumSpaceQuotas() throws Exception {
280    QuotaRetriever scanner = QuotaRetriever.open(TEST_UTIL.getConfiguration());
281    int numSpaceQuotas = 0;
282    for (QuotaSettings quotaSettings : scanner) {
283      if (quotaSettings.getQuotaType() == QuotaType.SPACE) {
284        numSpaceQuotas++;
285      }
286    }
287    return numSpaceQuotas;
288  }
289
290  public int getThrottleQuotas() throws Exception {
291    QuotaRetriever scanner = QuotaRetriever.open(TEST_UTIL.getConfiguration());
292    int throttleQuotas = 0;
293    for (QuotaSettings quotaSettings : scanner) {
294      if (quotaSettings.getQuotaType() == QuotaType.THROTTLE) {
295        throttleQuotas++;
296      }
297    }
298    return throttleQuotas;
299  }
300
301  private void createTable(Admin admin, TableName tn) throws Exception {
302    // Create a table
303    HTableDescriptor tableDesc = new HTableDescriptor(tn);
304    tableDesc.addFamily(new HColumnDescriptor("F1"));
305    admin.createTable(tableDesc);
306  }
307
308  private void dropTable(Admin admin, TableName tn) throws  Exception {
309    admin.disableTable(tn);
310    admin.deleteTable(tn);
311  }
312}