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;
022import static org.junit.Assert.fail;
023import static org.mockito.Mockito.mock;
024import static org.mockito.Mockito.when;
025
026import java.util.Arrays;
027import java.util.Collection;
028import java.util.Collections;
029import java.util.HashMap;
030import java.util.HashSet;
031import java.util.Map;
032import java.util.Set;
033import org.apache.hadoop.conf.Configuration;
034import org.apache.hadoop.hbase.HBaseClassTestRule;
035import org.apache.hadoop.hbase.HBaseConfiguration;
036import org.apache.hadoop.hbase.TableName;
037import org.apache.hadoop.hbase.client.Admin;
038import org.apache.hadoop.hbase.client.Connection;
039import org.apache.hadoop.hbase.client.RegionInfo;
040import org.apache.hadoop.hbase.quotas.QuotaObserverChore.TablesWithQuotas;
041import org.apache.hadoop.hbase.testclassification.SmallTests;
042import org.junit.Before;
043import org.junit.ClassRule;
044import org.junit.Test;
045import org.junit.experimental.categories.Category;
046
047import org.apache.hbase.thirdparty.com.google.common.collect.Multimap;
048
049/**
050 * Non-HBase cluster unit tests for {@link TablesWithQuotas}.
051 */
052@Category(SmallTests.class)
053public class TestTablesWithQuotas {
054
055  @ClassRule
056  public static final HBaseClassTestRule CLASS_RULE =
057    HBaseClassTestRule.forClass(TestTablesWithQuotas.class);
058
059  private Connection conn;
060  private Configuration conf;
061
062  @Before
063  public void setup() throws Exception {
064    conn = mock(Connection.class);
065    conf = HBaseConfiguration.create();
066  }
067
068  @Test
069  public void testImmutableGetters() {
070    Set<TableName> tablesWithTableQuotas = new HashSet<>();
071    Set<TableName> tablesWithNamespaceQuotas = new HashSet<>();
072    final TablesWithQuotas tables = new TablesWithQuotas(conn, conf);
073    for (int i = 0; i < 5; i++) {
074      TableName tn = TableName.valueOf("tn" + i);
075      tablesWithTableQuotas.add(tn);
076      tables.addTableQuotaTable(tn);
077    }
078    for (int i = 0; i < 3; i++) {
079      TableName tn = TableName.valueOf("tn_ns" + i);
080      tablesWithNamespaceQuotas.add(tn);
081      tables.addNamespaceQuotaTable(tn);
082    }
083    Set<TableName> actualTableQuotaTables = tables.getTableQuotaTables();
084    Set<TableName> actualNamespaceQuotaTables = tables.getNamespaceQuotaTables();
085    assertEquals(tablesWithTableQuotas, actualTableQuotaTables);
086    assertEquals(tablesWithNamespaceQuotas, actualNamespaceQuotaTables);
087    try {
088      actualTableQuotaTables.add(null);
089      fail("Should not be able to add an element");
090    } catch (UnsupportedOperationException e) {
091      // pass
092    }
093    try {
094      actualNamespaceQuotaTables.add(null);
095      fail("Should not be able to add an element");
096    } catch (UnsupportedOperationException e) {
097      // pass
098    }
099  }
100
101  @Test
102  public void testInsufficientlyReportedTableFiltering() throws Exception {
103    final Map<TableName, Integer> reportedRegions = new HashMap<>();
104    final Map<TableName, Integer> actualRegions = new HashMap<>();
105    final Configuration conf = HBaseConfiguration.create();
106    conf.setDouble(QuotaObserverChore.QUOTA_OBSERVER_CHORE_REPORT_PERCENT_KEY, 0.95);
107
108    TableName tooFewRegionsTable = TableName.valueOf("tn1");
109    TableName sufficientRegionsTable = TableName.valueOf("tn2");
110    TableName tooFewRegionsNamespaceTable = TableName.valueOf("ns1", "tn2");
111    TableName sufficientRegionsNamespaceTable = TableName.valueOf("ns1", "tn2");
112    final TablesWithQuotas tables = new TablesWithQuotas(conn, conf) {
113      @Override
114      Configuration getConfiguration() {
115        return conf;
116      }
117
118      @Override
119      int getNumRegions(TableName tableName) {
120        return actualRegions.get(tableName);
121      }
122
123      @Override
124      int getNumReportedRegions(TableName table, QuotaSnapshotStore<TableName> tableStore) {
125        return reportedRegions.get(table);
126      }
127    };
128    tables.addTableQuotaTable(tooFewRegionsTable);
129    tables.addTableQuotaTable(sufficientRegionsTable);
130    tables.addNamespaceQuotaTable(tooFewRegionsNamespaceTable);
131    tables.addNamespaceQuotaTable(sufficientRegionsNamespaceTable);
132
133    reportedRegions.put(tooFewRegionsTable, 5);
134    actualRegions.put(tooFewRegionsTable, 10);
135    reportedRegions.put(sufficientRegionsTable, 19);
136    actualRegions.put(sufficientRegionsTable, 20);
137    reportedRegions.put(tooFewRegionsNamespaceTable, 9);
138    actualRegions.put(tooFewRegionsNamespaceTable, 10);
139    reportedRegions.put(sufficientRegionsNamespaceTable, 98);
140    actualRegions.put(sufficientRegionsNamespaceTable, 100);
141
142    // Unused argument
143    tables.filterInsufficientlyReportedTables(null);
144    Set<TableName> filteredTablesWithTableQuotas = tables.getTableQuotaTables();
145    assertEquals(Collections.singleton(sufficientRegionsTable), filteredTablesWithTableQuotas);
146    Set<TableName> filteredTablesWithNamespaceQutoas = tables.getNamespaceQuotaTables();
147    assertEquals(Collections.singleton(sufficientRegionsNamespaceTable),
148      filteredTablesWithNamespaceQutoas);
149  }
150
151  @Test
152  public void testGetTablesByNamespace() {
153    final TablesWithQuotas tables = new TablesWithQuotas(conn, conf);
154    tables.addTableQuotaTable(TableName.valueOf("ignored1"));
155    tables.addTableQuotaTable(TableName.valueOf("ignored2"));
156    tables.addNamespaceQuotaTable(TableName.valueOf("ns1", "t1"));
157    tables.addNamespaceQuotaTable(TableName.valueOf("ns1", "t2"));
158    tables.addNamespaceQuotaTable(TableName.valueOf("ns1", "t3"));
159    tables.addNamespaceQuotaTable(TableName.valueOf("ns2", "t1"));
160    tables.addNamespaceQuotaTable(TableName.valueOf("ns2", "t2"));
161
162    Multimap<String, TableName> tablesByNamespace = tables.getTablesByNamespace();
163    Collection<TableName> tablesInNs = tablesByNamespace.get("ns1");
164    assertEquals(3, tablesInNs.size());
165    assertTrue("Unexpected results for ns1: " + tablesInNs,
166      tablesInNs.containsAll(Arrays.asList(TableName.valueOf("ns1", "t1"),
167        TableName.valueOf("ns1", "t2"), TableName.valueOf("ns1", "t3"))));
168    tablesInNs = tablesByNamespace.get("ns2");
169    assertEquals(2, tablesInNs.size());
170    assertTrue("Unexpected results for ns2: " + tablesInNs, tablesInNs
171      .containsAll(Arrays.asList(TableName.valueOf("ns2", "t1"), TableName.valueOf("ns2", "t2"))));
172  }
173
174  @Test
175  public void testFilteringMissingTables() throws Exception {
176    final TableName missingTable = TableName.valueOf("doesNotExist");
177    // Set up Admin to return null (match the implementation)
178    Admin admin = mock(Admin.class);
179    when(conn.getAdmin()).thenReturn(admin);
180    when(admin.getRegions(missingTable)).thenReturn(null);
181
182    QuotaObserverChore chore = mock(QuotaObserverChore.class);
183    Map<RegionInfo, Long> regionUsage = new HashMap<>();
184    TableQuotaSnapshotStore store = new TableQuotaSnapshotStore(conn, chore, regionUsage);
185
186    // A super dirty hack to verify that, after getting no regions for our table,
187    // we bail out and start processing the next element (which there is none).
188    final TablesWithQuotas tables = new TablesWithQuotas(conn, conf) {
189      @Override
190      int getNumReportedRegions(TableName table, QuotaSnapshotStore<TableName> tableStore) {
191        throw new RuntimeException("Should should not reach here");
192      }
193    };
194    tables.addTableQuotaTable(missingTable);
195
196    tables.filterInsufficientlyReportedTables(store);
197
198    final Set<TableName> tablesWithQuotas = tables.getTableQuotaTables();
199    assertTrue("Expected to find no tables, but found " + tablesWithQuotas,
200      tablesWithQuotas.isEmpty());
201  }
202}