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