View Javadoc

1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
3    * agreements. See the NOTICE file distributed with this work for additional information regarding
4    * copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the
5    * "License"); you may not use this file except in compliance with the License. You may obtain a
6    * copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable
7    * law or agreed to in writing, software distributed under the License is distributed on an "AS IS"
8    * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
9    * for the specific language governing permissions and limitations under the License.
10   */
11  
12  package org.apache.hadoop.hbase.quotas;
13  
14  import java.util.Arrays;
15  import java.util.List;
16  
17  import org.apache.hadoop.conf.Configuration;
18  import org.apache.hadoop.hbase.classification.InterfaceAudience;
19  import org.apache.hadoop.hbase.classification.InterfaceStability;
20  import org.apache.hadoop.hbase.client.Mutation;
21  import org.apache.hadoop.hbase.client.Result;
22  
23  @InterfaceAudience.Private
24  @InterfaceStability.Evolving
25  public class DefaultOperationQuota implements OperationQuota {
26    private final List<QuotaLimiter> limiters;
27    private final long writeCapacityUnit;
28    private final long readCapacityUnit;
29  
30    private long writeAvailable = 0;
31    private long readAvailable = 0;
32    private long writeConsumed = 0;
33    private long readConsumed = 0;
34    private long writeCapacityUnitConsumed = 0;
35    private long readCapacityUnitConsumed = 0;
36    private final long[] operationSize;
37  
38    public DefaultOperationQuota(final Configuration conf, final QuotaLimiter... limiters) {
39      this(conf, Arrays.asList(limiters));
40    }
41  
42    /**
43     * NOTE: The order matters. It should be something like [user, table, namespace, global]
44     */
45    public DefaultOperationQuota(final Configuration conf, final List<QuotaLimiter> limiters) {
46      this.writeCapacityUnit =
47          conf.getLong(QuotaUtil.WRITE_CAPACITY_UNIT_CONF_KEY, QuotaUtil.DEFAULT_WRITE_CAPACITY_UNIT);
48      this.readCapacityUnit =
49          conf.getLong(QuotaUtil.READ_CAPACITY_UNIT_CONF_KEY, QuotaUtil.DEFAULT_READ_CAPACITY_UNIT);
50      this.limiters = limiters;
51      int size = OperationType.values().length;
52      operationSize = new long[size];
53  
54      for (int i = 0; i < size; ++i) {
55        operationSize[i] = 0;
56      }
57    }
58  
59    @Override
60    public void checkQuota(int numWrites, int numReads, int numScans) throws RpcThrottlingException {
61      writeConsumed = estimateConsume(OperationType.MUTATE, numWrites, 100);
62      readConsumed = estimateConsume(OperationType.GET, numReads, 100);
63      readConsumed += estimateConsume(OperationType.SCAN, numScans, 1000);
64  
65      writeCapacityUnitConsumed = calculateWriteCapacityUnit(writeConsumed);
66      readCapacityUnitConsumed = calculateReadCapacityUnit(readConsumed);
67  
68      writeAvailable = Long.MAX_VALUE;
69      readAvailable = Long.MAX_VALUE;
70      for (final QuotaLimiter limiter : limiters) {
71        if (limiter.isBypass()) continue;
72  
73        limiter.checkQuota(numWrites, writeConsumed, numReads + numScans, readConsumed,
74          writeCapacityUnitConsumed, readCapacityUnitConsumed);
75        readAvailable = Math.min(readAvailable, limiter.getReadAvailable());
76        writeAvailable = Math.min(writeAvailable, limiter.getWriteAvailable());
77      }
78  
79      for (final QuotaLimiter limiter : limiters) {
80        limiter.grabQuota(numWrites, writeConsumed, numReads + numScans, readConsumed,
81          writeCapacityUnitConsumed, readCapacityUnitConsumed);
82      }
83    }
84  
85    @Override
86    public void close() {
87      // Adjust the quota consumed for the specified operation
88      long writeDiff = operationSize[OperationType.MUTATE.ordinal()] - writeConsumed;
89      long readDiff = operationSize[OperationType.GET.ordinal()]
90          + operationSize[OperationType.SCAN.ordinal()] - readConsumed;
91      long writeCapacityUnitDiff = calculateWriteCapacityUnitDiff(
92        operationSize[OperationType.MUTATE.ordinal()], writeConsumed);
93      long readCapacityUnitDiff = calculateReadCapacityUnitDiff(
94        operationSize[OperationType.GET.ordinal()] + operationSize[OperationType.SCAN.ordinal()],
95        readConsumed);
96  
97      for (final QuotaLimiter limiter : limiters) {
98        if (writeDiff != 0) {
99          limiter.consumeWrite(writeDiff, writeCapacityUnitDiff);
100       }
101       if (readDiff != 0) {
102         limiter.consumeRead(readDiff, readCapacityUnitDiff);
103       }
104     }
105   }
106 
107   @Override
108   public long getReadAvailable() {
109     return readAvailable;
110   }
111 
112   @Override
113   public long getWriteAvailable() {
114     return writeAvailable;
115   }
116 
117   @Override
118   public void addGetResult(final Result result) {
119     operationSize[OperationType.GET.ordinal()] += QuotaUtil.calculateResultSize(result);
120   }
121 
122   @Override
123   public void addScanResult(final List<Result> results) {
124     operationSize[OperationType.SCAN.ordinal()] += QuotaUtil.calculateResultSize(results);
125   }
126 
127   @Override
128   public void addMutation(final Mutation mutation) {
129     operationSize[OperationType.MUTATE.ordinal()] += QuotaUtil.calculateMutationSize(mutation);
130   }
131 
132   private long estimateConsume(final OperationType type, int numReqs, long avgSize) {
133     if (numReqs > 0) {
134       return avgSize * numReqs;
135     }
136     return 0;
137   }
138 
139   private long calculateWriteCapacityUnit(final long size) {
140     return (long) Math.ceil(size * 1.0 / this.writeCapacityUnit);
141   }
142 
143   private long calculateReadCapacityUnit(final long size) {
144     return (long) Math.ceil(size * 1.0 / this.readCapacityUnit);
145   }
146 
147   private long calculateWriteCapacityUnitDiff(final long actualSize, final long estimateSize) {
148     return calculateWriteCapacityUnit(actualSize) - calculateWriteCapacityUnit(estimateSize);
149   }
150 
151   private long calculateReadCapacityUnitDiff(final long actualSize, final long estimateSize) {
152     return calculateReadCapacityUnit(actualSize) - calculateReadCapacityUnit(estimateSize);
153   }
154 }