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.assertFalse;
022import static org.junit.Assert.assertNotNull;
023import static org.junit.Assert.assertNull;
024import static org.junit.Assert.assertTrue;
025import static org.junit.Assert.fail;
026
027import java.util.List;
028import java.util.concurrent.TimeUnit;
029import org.apache.hadoop.hbase.HBaseClassTestRule;
030import org.apache.hadoop.hbase.TableName;
031import org.apache.hadoop.hbase.testclassification.SmallTests;
032import org.junit.ClassRule;
033import org.junit.Test;
034import org.junit.experimental.categories.Category;
035
036import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil;
037import org.apache.hadoop.hbase.shaded.protobuf.generated.HBaseProtos;
038import org.apache.hadoop.hbase.shaded.protobuf.generated.QuotaProtos;
039import org.apache.hadoop.hbase.shaded.protobuf.generated.QuotaProtos.Quotas;
040import org.apache.hadoop.hbase.shaded.protobuf.generated.QuotaProtos.SpaceLimitRequest;
041import org.apache.hadoop.hbase.shaded.protobuf.generated.QuotaProtos.SpaceQuota;
042import org.apache.hadoop.hbase.shaded.protobuf.generated.QuotaProtos.Throttle;
043import org.apache.hadoop.hbase.shaded.protobuf.generated.QuotaProtos.TimedQuota;
044
045/**
046 * Test class for {@link QuotaSettingsFactory}.
047 */
048@Category(SmallTests.class)
049public class TestQuotaSettingsFactory {
050  @ClassRule
051  public static final HBaseClassTestRule CLASS_RULE =
052      HBaseClassTestRule.forClass(TestQuotaSettingsFactory.class);
053
054  @Test
055  public void testAllQuotasAddedToList() {
056    final SpaceQuota spaceQuota = SpaceQuota.newBuilder()
057        .setSoftLimit(1024L * 1024L * 1024L * 50L) // 50G
058        .setViolationPolicy(QuotaProtos.SpaceViolationPolicy.DISABLE) // Disable the table
059        .build();
060    final long readLimit = 1000;
061    final long writeLimit = 500;
062    final Throttle throttle = Throttle.newBuilder()
063        // 1000 read reqs/min
064        .setReadNum(TimedQuota.newBuilder().setSoftLimit(readLimit)
065            .setTimeUnit(HBaseProtos.TimeUnit.MINUTES).build())
066        // 500 write reqs/min
067        .setWriteNum(TimedQuota.newBuilder().setSoftLimit(writeLimit)
068            .setTimeUnit(HBaseProtos.TimeUnit.MINUTES).build())
069        .build();
070    final Quotas quotas = Quotas.newBuilder()
071        .setSpace(spaceQuota) // Set the FS quotas
072        .setThrottle(throttle) // Set some RPC limits
073        .build();
074    final TableName tn = TableName.valueOf("my_table");
075    List<QuotaSettings> settings = QuotaSettingsFactory.fromTableQuotas(tn, quotas);
076    assertEquals(3, settings.size());
077    boolean seenRead = false;
078    boolean seenWrite = false;
079    boolean seenSpace = false;
080    for (QuotaSettings setting : settings) {
081      if (setting instanceof ThrottleSettings) {
082        ThrottleSettings throttleSettings = (ThrottleSettings) setting;
083        switch (throttleSettings.getThrottleType()) {
084          case READ_NUMBER:
085            assertFalse("Should not have multiple read quotas", seenRead);
086            assertEquals(readLimit, throttleSettings.getSoftLimit());
087            assertEquals(TimeUnit.MINUTES, throttleSettings.getTimeUnit());
088            assertEquals(tn, throttleSettings.getTableName());
089            assertNull("Username should be null", throttleSettings.getUserName());
090            assertNull("Namespace should be null", throttleSettings.getNamespace());
091            assertNull("RegionServer should be null", throttleSettings.getRegionServer());
092            seenRead = true;
093            break;
094          case WRITE_NUMBER:
095            assertFalse("Should not have multiple write quotas", seenWrite);
096            assertEquals(writeLimit, throttleSettings.getSoftLimit());
097            assertEquals(TimeUnit.MINUTES, throttleSettings.getTimeUnit());
098            assertEquals(tn, throttleSettings.getTableName());
099            assertNull("Username should be null", throttleSettings.getUserName());
100            assertNull("Namespace should be null", throttleSettings.getNamespace());
101            assertNull("RegionServer should be null", throttleSettings.getRegionServer());
102            seenWrite = true;
103            break;
104          default:
105            fail("Unexpected throttle type: " + throttleSettings.getThrottleType());
106        }
107      } else if (setting instanceof SpaceLimitSettings) {
108        assertFalse("Should not have multiple space quotas", seenSpace);
109        SpaceLimitSettings spaceLimit = (SpaceLimitSettings) setting;
110        assertEquals(tn, spaceLimit.getTableName());
111        assertNull("Username should be null", spaceLimit.getUserName());
112        assertNull("Namespace should be null", spaceLimit.getNamespace());
113        assertNull("RegionServer should be null", spaceLimit.getRegionServer());
114        assertTrue("SpaceLimitSettings should have a SpaceQuota", spaceLimit.getProto().hasQuota());
115        assertEquals(spaceQuota, spaceLimit.getProto().getQuota());
116        seenSpace = true;
117      } else {
118        fail("Unexpected QuotaSettings implementation: " + setting.getClass());
119      }
120    }
121    assertTrue("Should have seen a read quota", seenRead);
122    assertTrue("Should have seen a write quota", seenWrite);
123    assertTrue("Should have seen a space quota", seenSpace);
124  }
125
126  @Test(expected = IllegalArgumentException.class)
127  public void testNeitherTableNorNamespace() {
128    final SpaceQuota spaceQuota = SpaceQuota.newBuilder()
129        .setSoftLimit(1L)
130        .setViolationPolicy(QuotaProtos.SpaceViolationPolicy.DISABLE)
131        .build();
132    QuotaSettingsFactory.fromSpace(null, null, spaceQuota);
133  }
134
135  @Test(expected = IllegalArgumentException.class)
136  public void testBothTableAndNamespace() {
137    final SpaceQuota spaceQuota = SpaceQuota.newBuilder()
138        .setSoftLimit(1L)
139        .setViolationPolicy(QuotaProtos.SpaceViolationPolicy.DISABLE)
140        .build();
141    QuotaSettingsFactory.fromSpace(TableName.valueOf("foo"), "bar", spaceQuota);
142  }
143
144  @Test
145  public void testSpaceLimitSettings() {
146    final TableName tableName = TableName.valueOf("foo");
147    final long sizeLimit = 1024L * 1024L * 1024L * 75; // 75GB
148    final SpaceViolationPolicy violationPolicy = SpaceViolationPolicy.NO_INSERTS;
149    QuotaSettings settings =
150        QuotaSettingsFactory.limitTableSpace(tableName, sizeLimit, violationPolicy);
151    assertNotNull("QuotaSettings should not be null", settings);
152    assertTrue("Should be an instance of SpaceLimitSettings",
153        settings instanceof SpaceLimitSettings);
154    SpaceLimitSettings spaceLimitSettings = (SpaceLimitSettings) settings;
155    SpaceLimitRequest protoRequest = spaceLimitSettings.getProto();
156    assertTrue("Request should have a SpaceQuota", protoRequest.hasQuota());
157    SpaceQuota quota = protoRequest.getQuota();
158    assertEquals(sizeLimit, quota.getSoftLimit());
159    assertEquals(violationPolicy, ProtobufUtil.toViolationPolicy(quota.getViolationPolicy()));
160    assertFalse("The remove attribute should be false", quota.getRemove());
161  }
162
163  @Test
164  public void testSpaceLimitSettingsForDeletes() {
165    final String ns = "ns1";
166    final TableName tn = TableName.valueOf("tn1");
167    QuotaSettings nsSettings = QuotaSettingsFactory.removeNamespaceSpaceLimit(ns);
168    assertNotNull("QuotaSettings should not be null", nsSettings);
169    assertTrue("Should be an instance of SpaceLimitSettings",
170        nsSettings instanceof SpaceLimitSettings);
171    SpaceLimitRequest nsProto = ((SpaceLimitSettings) nsSettings).getProto();
172    assertTrue("Request should have a SpaceQuota", nsProto.hasQuota());
173    assertTrue("The remove attribute should be true", nsProto.getQuota().getRemove());
174
175    QuotaSettings tableSettings = QuotaSettingsFactory.removeTableSpaceLimit(tn);
176    assertNotNull("QuotaSettings should not be null", tableSettings);
177    assertTrue("Should be an instance of SpaceLimitSettings",
178        tableSettings instanceof SpaceLimitSettings);
179    SpaceLimitRequest tableProto = ((SpaceLimitSettings) tableSettings).getProto();
180    assertTrue("Request should have a SpaceQuota", tableProto.hasQuota());
181    assertTrue("The remove attribute should be true", tableProto.getQuota().getRemove());
182  }
183}