View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to you under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    * http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.hadoop.hbase.quotas;
18  
19  import static org.junit.Assert.assertEquals;
20  import static org.junit.Assert.assertTrue;
21  import static org.junit.Assert.fail;
22  import static org.mockito.Mockito.mock;
23  import static org.mockito.Mockito.when;
24  
25  import java.util.Arrays;
26  import java.util.Collection;
27  import java.util.Collections;
28  import java.util.HashMap;
29  import java.util.HashSet;
30  import java.util.Map;
31  import java.util.Set;
32  
33  import org.apache.hadoop.conf.Configuration;
34  import org.apache.hadoop.hbase.HBaseConfiguration;
35  import org.apache.hadoop.hbase.HRegionInfo;
36  import org.apache.hadoop.hbase.TableName;
37  import org.apache.hadoop.hbase.client.Admin;
38  import org.apache.hadoop.hbase.client.Connection;
39  import org.apache.hadoop.hbase.quotas.QuotaObserverChore.TablesWithQuotas;
40  import org.apache.hadoop.hbase.testclassification.SmallTests;
41  import org.junit.Before;
42  import org.junit.Test;
43  import org.junit.experimental.categories.Category;
44  
45  import com.google.common.collect.Multimap;
46  
47  /**
48   * Non-HBase cluster unit tests for {@link TablesWithQuotas}.
49   */
50  @Category(SmallTests.class)
51  public class TestTablesWithQuotas {
52    private Connection conn;
53    private Configuration conf;
54  
55    @Before
56    public void setup() throws Exception {
57      conn = mock(Connection.class);
58      conf = HBaseConfiguration.create();
59    }
60  
61    @Test
62    public void testImmutableGetters() {
63      Set<TableName> tablesWithTableQuotas = new HashSet<>();
64      Set<TableName> tablesWithNamespaceQuotas = new HashSet<>();
65      final TablesWithQuotas tables = new TablesWithQuotas(conn, conf);
66      for (int i = 0; i < 5; i++) {
67        TableName tn = TableName.valueOf("tn" + i);
68        tablesWithTableQuotas.add(tn);
69        tables.addTableQuotaTable(tn);
70      }
71      for (int i = 0; i < 3; i++) {
72        TableName tn = TableName.valueOf("tn_ns" + i);
73        tablesWithNamespaceQuotas.add(tn);
74        tables.addNamespaceQuotaTable(tn);
75      }
76      Set<TableName> actualTableQuotaTables = tables.getTableQuotaTables();
77      Set<TableName> actualNamespaceQuotaTables = tables.getNamespaceQuotaTables();
78      assertEquals(tablesWithTableQuotas, actualTableQuotaTables);
79      assertEquals(tablesWithNamespaceQuotas, actualNamespaceQuotaTables);
80      try {
81        actualTableQuotaTables.add(null);
82        fail("Should not be able to add an element");
83      } catch (UnsupportedOperationException e) {
84        // pass
85      }
86      try {
87        actualNamespaceQuotaTables.add(null);
88        fail("Should not be able to add an element");
89      } catch (UnsupportedOperationException e) {
90        // pass
91      }
92    }
93  
94    @Test
95    public void testInsufficientlyReportedTableFiltering() throws Exception {
96      final Map<TableName,Integer> reportedRegions = new HashMap<>();
97      final Map<TableName,Integer> actualRegions = new HashMap<>();
98      final Configuration conf = HBaseConfiguration.create();
99      conf.setDouble(QuotaObserverChore.QUOTA_OBSERVER_CHORE_REPORT_PERCENT_KEY, 0.95);
100 
101     TableName tooFewRegionsTable = TableName.valueOf("tn1");
102     TableName sufficientRegionsTable = TableName.valueOf("tn2");
103     TableName tooFewRegionsNamespaceTable = TableName.valueOf("ns1", "tn2");
104     TableName sufficientRegionsNamespaceTable = TableName.valueOf("ns1", "tn2");
105     final TablesWithQuotas tables = new TablesWithQuotas(conn, conf) {
106       @Override
107       Configuration getConfiguration() {
108         return conf;
109       }
110 
111       @Override
112       int getNumRegions(TableName tableName) {
113         return actualRegions.get(tableName);
114       }
115 
116       @Override
117       int getNumReportedRegions(TableName table, QuotaSnapshotStore<TableName> tableStore) {
118         return reportedRegions.get(table);
119       }
120     };
121     tables.addTableQuotaTable(tooFewRegionsTable);
122     tables.addTableQuotaTable(sufficientRegionsTable);
123     tables.addNamespaceQuotaTable(tooFewRegionsNamespaceTable);
124     tables.addNamespaceQuotaTable(sufficientRegionsNamespaceTable);
125 
126     reportedRegions.put(tooFewRegionsTable, 5);
127     actualRegions.put(tooFewRegionsTable, 10);
128     reportedRegions.put(sufficientRegionsTable, 19);
129     actualRegions.put(sufficientRegionsTable, 20);
130     reportedRegions.put(tooFewRegionsNamespaceTable, 9);
131     actualRegions.put(tooFewRegionsNamespaceTable, 10);
132     reportedRegions.put(sufficientRegionsNamespaceTable, 98);
133     actualRegions.put(sufficientRegionsNamespaceTable, 100);
134 
135     // Unused argument
136     tables.filterInsufficientlyReportedTables(null);
137     Set<TableName> filteredTablesWithTableQuotas = tables.getTableQuotaTables();
138     assertEquals(Collections.singleton(sufficientRegionsTable), filteredTablesWithTableQuotas);
139     Set<TableName> filteredTablesWithNamespaceQutoas = tables.getNamespaceQuotaTables();
140     assertEquals(Collections.singleton(sufficientRegionsNamespaceTable), filteredTablesWithNamespaceQutoas);
141   }
142 
143   @Test
144   public void testGetTablesByNamespace() {
145     final TablesWithQuotas tables = new TablesWithQuotas(conn, conf);
146     tables.addTableQuotaTable(TableName.valueOf("ignored1"));
147     tables.addTableQuotaTable(TableName.valueOf("ignored2"));
148     tables.addNamespaceQuotaTable(TableName.valueOf("ns1", "t1"));
149     tables.addNamespaceQuotaTable(TableName.valueOf("ns1", "t2"));
150     tables.addNamespaceQuotaTable(TableName.valueOf("ns1", "t3"));
151     tables.addNamespaceQuotaTable(TableName.valueOf("ns2", "t1"));
152     tables.addNamespaceQuotaTable(TableName.valueOf("ns2", "t2"));
153 
154     Multimap<String,TableName> tablesByNamespace = tables.getTablesByNamespace();
155     Collection<TableName> tablesInNs = tablesByNamespace.get("ns1");
156     assertEquals(3, tablesInNs.size());
157     assertTrue("Unexpected results for ns1: " + tablesInNs,
158         tablesInNs.containsAll(Arrays.asList(
159             TableName.valueOf("ns1", "t1"),
160             TableName.valueOf("ns1", "t2"),
161             TableName.valueOf("ns1", "t3"))));
162     tablesInNs = tablesByNamespace.get("ns2");
163     assertEquals(2, tablesInNs.size());
164     assertTrue("Unexpected results for ns2: " + tablesInNs,
165         tablesInNs.containsAll(Arrays.asList(
166             TableName.valueOf("ns2", "t1"),
167             TableName.valueOf("ns2", "t2"))));
168   }
169 
170   @Test
171   public void testFilteringMissingTables() throws Exception {
172     final TableName missingTable = TableName.valueOf("doesNotExist");
173     // Set up Admin to return null (match the implementation)
174     Admin admin = mock(Admin.class);
175     when(conn.getAdmin()).thenReturn(admin);
176     when(admin.getTableRegions(missingTable)).thenReturn(null);
177 
178     QuotaObserverChore chore = mock(QuotaObserverChore.class);
179     Map<HRegionInfo,Long> regionUsage = new HashMap<>();
180     TableQuotaSnapshotStore store = new TableQuotaSnapshotStore(conn, chore, regionUsage);
181 
182     // A super dirty hack to verify that, after getting no regions for our table,
183     // we bail out and start processing the next element (which there is none).
184     final TablesWithQuotas tables = new TablesWithQuotas(conn, conf) {
185       @Override
186       int getNumReportedRegions(TableName table, QuotaSnapshotStore<TableName> tableStore) {
187         throw new RuntimeException("Should should not reach here");
188       }
189     };
190     tables.addTableQuotaTable(missingTable);
191 
192     tables.filterInsufficientlyReportedTables(store);
193 
194     final Set<TableName> tablesWithQuotas = tables.getTableQuotaTables();
195     assertTrue(
196         "Expected to find no tables, but found " + tablesWithQuotas, tablesWithQuotas.isEmpty());
197   }
198 }