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.hadoop.hbase.util.Bytes.toBytes; 021import static org.junit.Assert.fail; 022import static org.mockito.Matchers.any; 023import static org.mockito.Mockito.doCallRealMethod; 024import static org.mockito.Mockito.mock; 025import static org.mockito.Mockito.never; 026import static org.mockito.Mockito.verify; 027import static org.mockito.Mockito.when; 028 029import java.io.IOException; 030import java.util.ArrayList; 031import java.util.Collections; 032import java.util.HashMap; 033import java.util.List; 034import java.util.Map; 035import java.util.Map.Entry; 036import org.apache.hadoop.conf.Configuration; 037import org.apache.hadoop.hbase.Cell; 038import org.apache.hadoop.hbase.HBaseClassTestRule; 039import org.apache.hadoop.hbase.HBaseConfiguration; 040import org.apache.hadoop.hbase.KeyValue; 041import org.apache.hadoop.hbase.TableName; 042import org.apache.hadoop.hbase.client.Connection; 043import org.apache.hadoop.hbase.client.Result; 044import org.apache.hadoop.hbase.client.ResultScanner; 045import org.apache.hadoop.hbase.client.Scan; 046import org.apache.hadoop.hbase.client.Table; 047import org.apache.hadoop.hbase.quotas.SpaceQuotaSnapshot.SpaceQuotaStatus; 048import org.apache.hadoop.hbase.regionserver.RegionServerServices; 049import org.apache.hadoop.hbase.testclassification.SmallTests; 050import org.junit.Before; 051import org.junit.ClassRule; 052import org.junit.Test; 053import org.junit.experimental.categories.Category; 054 055/** 056 * Test class for {@link SpaceQuotaRefresherChore}. 057 */ 058@Category(SmallTests.class) 059public class TestSpaceQuotaViolationPolicyRefresherChore { 060 061 @ClassRule 062 public static final HBaseClassTestRule CLASS_RULE = 063 HBaseClassTestRule.forClass(TestSpaceQuotaViolationPolicyRefresherChore.class); 064 065 private RegionServerSpaceQuotaManager manager; 066 private RegionServerServices rss; 067 private SpaceQuotaRefresherChore chore; 068 private Configuration conf; 069 private Connection conn; 070 071 @SuppressWarnings("unchecked") 072 @Before 073 public void setup() throws IOException { 074 conf = HBaseConfiguration.create(); 075 rss = mock(RegionServerServices.class); 076 manager = mock(RegionServerSpaceQuotaManager.class); 077 conn = mock(Connection.class); 078 when(manager.getRegionServerServices()).thenReturn(rss); 079 when(rss.getConfiguration()).thenReturn(conf); 080 081 chore = mock(SpaceQuotaRefresherChore.class); 082 when(chore.getConnection()).thenReturn(conn); 083 when(chore.getManager()).thenReturn(manager); 084 when(chore.checkQuotaTableExists()).thenReturn(true); 085 doCallRealMethod().when(chore).chore(); 086 when(chore.isInViolation(any())).thenCallRealMethod(); 087 doCallRealMethod().when(chore).extractQuotaSnapshot(any(), any()); 088 } 089 090 @Test 091 public void testPoliciesAreEnforced() throws IOException { 092 // Create a number of policies that should be enforced (usage > limit) 093 final Map<TableName, SpaceQuotaSnapshot> policiesToEnforce = new HashMap<>(); 094 policiesToEnforce.put(TableName.valueOf("table1"), 095 new SpaceQuotaSnapshot(new SpaceQuotaStatus(SpaceViolationPolicy.DISABLE), 1024L, 512L)); 096 policiesToEnforce.put(TableName.valueOf("table2"), 097 new SpaceQuotaSnapshot(new SpaceQuotaStatus(SpaceViolationPolicy.NO_INSERTS), 2048L, 512L)); 098 policiesToEnforce.put(TableName.valueOf("table3"), 099 new SpaceQuotaSnapshot(new SpaceQuotaStatus(SpaceViolationPolicy.NO_WRITES), 4096L, 512L)); 100 policiesToEnforce.put(TableName.valueOf("table4"), new SpaceQuotaSnapshot( 101 new SpaceQuotaStatus(SpaceViolationPolicy.NO_WRITES_COMPACTIONS), 8192L, 512L)); 102 103 // No active enforcements 104 when(manager.copyQuotaSnapshots()).thenReturn(Collections.emptyMap()); 105 // Policies to enforce 106 when(chore.fetchSnapshotsFromQuotaTable()).thenReturn(policiesToEnforce); 107 108 chore.chore(); 109 110 for (Entry<TableName, SpaceQuotaSnapshot> entry : policiesToEnforce.entrySet()) { 111 // Ensure we enforce the policy 112 verify(manager).enforceViolationPolicy(entry.getKey(), entry.getValue()); 113 // Don't disable any policies 114 verify(manager, never()).disableViolationPolicyEnforcement(entry.getKey()); 115 } 116 } 117 118 @Test 119 public void testOldPoliciesAreRemoved() throws IOException { 120 final Map<TableName, SpaceQuotaSnapshot> previousPolicies = new HashMap<>(); 121 previousPolicies.put(TableName.valueOf("table3"), 122 new SpaceQuotaSnapshot(new SpaceQuotaStatus(SpaceViolationPolicy.NO_WRITES), 4096L, 512L)); 123 previousPolicies.put(TableName.valueOf("table4"), 124 new SpaceQuotaSnapshot(new SpaceQuotaStatus(SpaceViolationPolicy.NO_WRITES), 8192L, 512L)); 125 126 final Map<TableName, SpaceQuotaSnapshot> policiesToEnforce = new HashMap<>(); 127 policiesToEnforce.put(TableName.valueOf("table1"), 128 new SpaceQuotaSnapshot(new SpaceQuotaStatus(SpaceViolationPolicy.DISABLE), 1024L, 512L)); 129 policiesToEnforce.put(TableName.valueOf("table2"), 130 new SpaceQuotaSnapshot(new SpaceQuotaStatus(SpaceViolationPolicy.NO_INSERTS), 2048L, 512L)); 131 policiesToEnforce.put(TableName.valueOf("table3"), 132 new SpaceQuotaSnapshot(SpaceQuotaStatus.notInViolation(), 256L, 512L)); 133 policiesToEnforce.put(TableName.valueOf("table4"), 134 new SpaceQuotaSnapshot(SpaceQuotaStatus.notInViolation(), 128L, 512L)); 135 136 // No active enforcements 137 when(manager.copyQuotaSnapshots()).thenReturn(previousPolicies); 138 // Policies to enforce 139 when(chore.fetchSnapshotsFromQuotaTable()).thenReturn(policiesToEnforce); 140 141 chore.chore(); 142 143 verify(manager).enforceViolationPolicy(TableName.valueOf("table1"), 144 policiesToEnforce.get(TableName.valueOf("table1"))); 145 verify(manager).enforceViolationPolicy(TableName.valueOf("table2"), 146 policiesToEnforce.get(TableName.valueOf("table2"))); 147 148 verify(manager).disableViolationPolicyEnforcement(TableName.valueOf("table3")); 149 verify(manager).disableViolationPolicyEnforcement(TableName.valueOf("table4")); 150 } 151 152 @Test 153 public void testNewPolicyOverridesOld() throws IOException { 154 final Map<TableName, SpaceQuotaSnapshot> policiesToEnforce = new HashMap<>(); 155 policiesToEnforce.put(TableName.valueOf("table1"), 156 new SpaceQuotaSnapshot(new SpaceQuotaStatus(SpaceViolationPolicy.DISABLE), 1024L, 512L)); 157 policiesToEnforce.put(TableName.valueOf("table2"), 158 new SpaceQuotaSnapshot(new SpaceQuotaStatus(SpaceViolationPolicy.NO_WRITES), 2048L, 512L)); 159 policiesToEnforce.put(TableName.valueOf("table3"), 160 new SpaceQuotaSnapshot(new SpaceQuotaStatus(SpaceViolationPolicy.NO_INSERTS), 4096L, 512L)); 161 162 final Map<TableName, SpaceQuotaSnapshot> previousPolicies = new HashMap<>(); 163 previousPolicies.put(TableName.valueOf("table1"), 164 new SpaceQuotaSnapshot(new SpaceQuotaStatus(SpaceViolationPolicy.NO_WRITES), 8192L, 512L)); 165 166 // No active enforcements 167 when(manager.getActivePoliciesAsMap()).thenReturn(previousPolicies); 168 // Policies to enforce 169 when(chore.fetchSnapshotsFromQuotaTable()).thenReturn(policiesToEnforce); 170 171 chore.chore(); 172 173 for (Entry<TableName, SpaceQuotaSnapshot> entry : policiesToEnforce.entrySet()) { 174 verify(manager).enforceViolationPolicy(entry.getKey(), entry.getValue()); 175 } 176 verify(manager, never()).disableViolationPolicyEnforcement(TableName.valueOf("table1")); 177 } 178 179 @Test 180 public void testMissingAllColumns() throws IOException { 181 when(chore.fetchSnapshotsFromQuotaTable()).thenCallRealMethod(); 182 ResultScanner scanner = mock(ResultScanner.class); 183 Table quotaTable = mock(Table.class); 184 when(conn.getTable(QuotaUtil.QUOTA_TABLE_NAME)).thenReturn(quotaTable); 185 when(quotaTable.getScanner(any(Scan.class))).thenReturn(scanner); 186 187 List<Result> results = new ArrayList<>(); 188 results.add(Result.create(Collections.emptyList())); 189 when(scanner.iterator()).thenReturn(results.iterator()); 190 try { 191 chore.fetchSnapshotsFromQuotaTable(); 192 fail("Expected an IOException, but did not receive one."); 193 } catch (IOException e) { 194 // Expected an error because we had no cells in the row. 195 // This should only happen due to programmer error. 196 } 197 } 198 199 @Test 200 public void testMissingDesiredColumn() throws IOException { 201 when(chore.fetchSnapshotsFromQuotaTable()).thenCallRealMethod(); 202 ResultScanner scanner = mock(ResultScanner.class); 203 Table quotaTable = mock(Table.class); 204 when(conn.getTable(QuotaUtil.QUOTA_TABLE_NAME)).thenReturn(quotaTable); 205 when(quotaTable.getScanner(any(Scan.class))).thenReturn(scanner); 206 207 List<Result> results = new ArrayList<>(); 208 // Give a column that isn't the one we want 209 Cell c = new KeyValue(toBytes("t:inviolation"), toBytes("q"), toBytes("s"), new byte[0]); 210 results.add(Result.create(Collections.singletonList(c))); 211 when(scanner.iterator()).thenReturn(results.iterator()); 212 try { 213 chore.fetchSnapshotsFromQuotaTable(); 214 fail("Expected an IOException, but did not receive one."); 215 } catch (IOException e) { 216 // Expected an error because we were missing the column we expected in this row. 217 // This should only happen due to programmer error. 218 } 219 } 220 221 @Test 222 public void testParsingError() throws IOException { 223 when(chore.fetchSnapshotsFromQuotaTable()).thenCallRealMethod(); 224 ResultScanner scanner = mock(ResultScanner.class); 225 Table quotaTable = mock(Table.class); 226 when(conn.getTable(QuotaUtil.QUOTA_TABLE_NAME)).thenReturn(quotaTable); 227 when(quotaTable.getScanner(any(Scan.class))).thenReturn(scanner); 228 229 List<Result> results = new ArrayList<>(); 230 Cell c = new KeyValue(toBytes("t:inviolation"), toBytes("u"), toBytes("v"), new byte[0]); 231 results.add(Result.create(Collections.singletonList(c))); 232 when(scanner.iterator()).thenReturn(results.iterator()); 233 try { 234 chore.fetchSnapshotsFromQuotaTable(); 235 fail("Expected an IOException, but did not receive one."); 236 } catch (IOException e) { 237 // We provided a garbage serialized protobuf message (empty byte array), this should 238 // in turn throw an IOException 239 } 240 } 241}