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