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.apache.hbase.thirdparty.com.google.common.collect.Iterables.size; 021import static org.junit.Assert.assertEquals; 022import static org.junit.Assert.assertNull; 023import static org.mockito.Matchers.any; 024import static org.mockito.Mockito.mock; 025import static org.mockito.Mockito.when; 026 027import java.io.IOException; 028import java.util.HashMap; 029import java.util.Map; 030import java.util.concurrent.atomic.AtomicReference; 031import org.apache.hadoop.hbase.HBaseClassTestRule; 032import org.apache.hadoop.hbase.HRegionInfo; 033import org.apache.hadoop.hbase.NamespaceDescriptor; 034import org.apache.hadoop.hbase.TableName; 035import org.apache.hadoop.hbase.client.Connection; 036import org.apache.hadoop.hbase.client.Get; 037import org.apache.hadoop.hbase.client.RegionInfo; 038import org.apache.hadoop.hbase.client.RegionInfoBuilder; 039import org.apache.hadoop.hbase.client.Result; 040import org.apache.hadoop.hbase.client.Table; 041import org.apache.hadoop.hbase.testclassification.SmallTests; 042import org.apache.hadoop.hbase.util.Bytes; 043import org.junit.Before; 044import org.junit.ClassRule; 045import org.junit.Test; 046import org.junit.experimental.categories.Category; 047import org.mockito.invocation.InvocationOnMock; 048import org.mockito.stubbing.Answer; 049 050import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil; 051import org.apache.hadoop.hbase.shaded.protobuf.generated.QuotaProtos; 052import org.apache.hadoop.hbase.shaded.protobuf.generated.QuotaProtos.Quotas; 053import org.apache.hadoop.hbase.shaded.protobuf.generated.QuotaProtos.SpaceQuota; 054 055/** 056 * Test class for {@link NamespaceQuotaSnapshotStore}. 057 */ 058@Category(SmallTests.class) 059public class TestNamespaceQuotaViolationStore { 060 061 @ClassRule 062 public static final HBaseClassTestRule CLASS_RULE = 063 HBaseClassTestRule.forClass(TestNamespaceQuotaViolationStore.class); 064 065 private static final long ONE_MEGABYTE = 1024L * 1024L; 066 067 private Connection conn; 068 private QuotaObserverChore chore; 069 private Map<RegionInfo, Long> regionReports; 070 private NamespaceQuotaSnapshotStore store; 071 072 @Before 073 public void setup() { 074 conn = mock(Connection.class); 075 chore = mock(QuotaObserverChore.class); 076 regionReports = new HashMap<>(); 077 store = new NamespaceQuotaSnapshotStore(conn, chore, regionReports); 078 } 079 080 @Test 081 public void testGetSpaceQuota() throws Exception { 082 NamespaceQuotaSnapshotStore mockStore = mock(NamespaceQuotaSnapshotStore.class); 083 when(mockStore.getSpaceQuota(any())).thenCallRealMethod(); 084 085 Quotas quotaWithSpace = Quotas.newBuilder().setSpace( 086 SpaceQuota.newBuilder() 087 .setSoftLimit(1024L) 088 .setViolationPolicy(QuotaProtos.SpaceViolationPolicy.DISABLE) 089 .build()) 090 .build(); 091 Quotas quotaWithoutSpace = Quotas.newBuilder().build(); 092 093 AtomicReference<Quotas> quotaRef = new AtomicReference<>(); 094 when(mockStore.getQuotaForNamespace(any())).then(new Answer<Quotas>() { 095 @Override 096 public Quotas answer(InvocationOnMock invocation) throws Throwable { 097 return quotaRef.get(); 098 } 099 }); 100 101 quotaRef.set(quotaWithSpace); 102 assertEquals(quotaWithSpace.getSpace(), mockStore.getSpaceQuota("ns")); 103 quotaRef.set(quotaWithoutSpace); 104 assertNull(mockStore.getSpaceQuota("ns")); 105 } 106 107 @Test 108 public void testTargetViolationState() throws IOException { 109 mockNoSnapshotSizes(); 110 final String NS = "ns"; 111 TableName tn1 = TableName.valueOf(NS, "tn1"); 112 TableName tn2 = TableName.valueOf(NS, "tn2"); 113 TableName tn3 = TableName.valueOf("tn3"); 114 SpaceQuota quota = SpaceQuota.newBuilder() 115 .setSoftLimit(ONE_MEGABYTE) 116 .setViolationPolicy(ProtobufUtil.toProtoViolationPolicy(SpaceViolationPolicy.DISABLE)) 117 .build(); 118 119 // Create some junk data to filter. Makes sure it's so large that it would 120 // immediately violate the quota. 121 for (int i = 0; i < 3; i++) { 122 123 regionReports.put(RegionInfoBuilder.newBuilder(tn3) 124 .setStartKey(Bytes.toBytes(i)) 125 .setEndKey(Bytes.toBytes(i + 1)) 126 .build(), 127 5L * ONE_MEGABYTE); 128 } 129 130 regionReports.put(RegionInfoBuilder.newBuilder(tn1) 131 .setStartKey(Bytes.toBytes(0)) 132 .setEndKey(Bytes.toBytes(1)) 133 .build(), 1024L * 512L); 134 regionReports.put(RegionInfoBuilder.newBuilder(tn1) 135 .setStartKey(Bytes.toBytes(1)) 136 .setEndKey(Bytes.toBytes(2)) 137 .build(), 1024L * 256L); 138 139 // Below the quota 140 assertEquals(false, store.getTargetState(NS, quota).getQuotaStatus().isInViolation()); 141 142 regionReports.put(RegionInfoBuilder.newBuilder(tn2) 143 .setStartKey(Bytes.toBytes(2)) 144 .setEndKey(Bytes.toBytes(3)) 145 .build(), 1024L * 256L); 146 147 // Equal to the quota is still in observance 148 assertEquals(false, store.getTargetState(NS, quota).getQuotaStatus().isInViolation()); 149 150 regionReports.put(RegionInfoBuilder.newBuilder(tn2) 151 .setStartKey(Bytes.toBytes(3)) 152 .setEndKey(Bytes.toBytes(4)) 153 .build(), 1024L); 154 155 // Exceeds the quota, should be in violation 156 assertEquals(true, store.getTargetState(NS, quota).getQuotaStatus().isInViolation()); 157 assertEquals( 158 SpaceViolationPolicy.DISABLE, store.getTargetState(NS, quota).getQuotaStatus().getPolicy()); 159 } 160 161 @Test 162 public void testFilterRegionsByNamespace() { 163 TableName tn1 = TableName.valueOf("foo"); 164 TableName tn2 = TableName.valueOf("sn", "bar"); 165 TableName tn3 = TableName.valueOf("ns", "foo"); 166 TableName tn4 = TableName.valueOf("ns", "bar"); 167 168 assertEquals(0, size(store.filterBySubject("asdf"))); 169 170 for (int i = 0; i < 5; i++) { 171 regionReports.put(RegionInfoBuilder.newBuilder(tn1) 172 .setStartKey(Bytes.toBytes(i)) 173 .setEndKey(Bytes.toBytes(i + 1)) 174 .build(), 0L); 175 } 176 for (int i = 0; i < 3; i++) { 177 regionReports.put(RegionInfoBuilder.newBuilder(tn2) 178 .setStartKey(Bytes.toBytes(i)) 179 .setEndKey(Bytes.toBytes(i + 1)) 180 .build(), 0L); 181 } 182 for (int i = 0; i < 10; i++) { 183 regionReports.put(RegionInfoBuilder.newBuilder(tn3) 184 .setStartKey(Bytes.toBytes(i)) 185 .setEndKey(Bytes.toBytes(i + 1)) 186 .build(), 0L); 187 } 188 for (int i = 0; i < 8; i++) { 189 regionReports.put(RegionInfoBuilder.newBuilder(tn4) 190 .setStartKey(Bytes.toBytes(i)) 191 .setEndKey(Bytes.toBytes(i + 1)) 192 .build(), 0L); 193 } 194 assertEquals(26, regionReports.size()); 195 assertEquals(5, size(store.filterBySubject(NamespaceDescriptor.DEFAULT_NAMESPACE_NAME_STR))); 196 assertEquals(3, size(store.filterBySubject("sn"))); 197 assertEquals(18, size(store.filterBySubject("ns"))); 198 } 199 200 void mockNoSnapshotSizes() throws IOException { 201 Table quotaTable = mock(Table.class); 202 when(conn.getTable(QuotaTableUtil.QUOTA_TABLE_NAME)).thenReturn(quotaTable); 203 when(quotaTable.get(any(Get.class))).thenReturn(new Result()); 204 } 205}