1
2
3
4
5
6
7
8
9
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
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
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 }