1
2
3
4
5
6
7
8
9
10
11
12 package org.apache.hadoop.hbase.quotas;
13
14 import java.util.concurrent.TimeUnit;
15
16 import org.apache.hadoop.hbase.classification.InterfaceAudience;
17 import org.apache.hadoop.hbase.classification.InterfaceStability;
18
19 import com.google.common.annotations.VisibleForTesting;
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39 @InterfaceAudience.Private
40 @InterfaceStability.Evolving
41 public abstract class RateLimiter {
42 public static final String QUOTA_RATE_LIMITER_CONF_KEY = "hbase.quota.rate.limiter";
43 private long tunit = 1000;
44 private long limit = Long.MAX_VALUE;
45 private long avail = Long.MAX_VALUE;
46
47
48
49
50
51
52 abstract long refill(long limit);
53
54
55
56
57
58
59
60
61 abstract long getWaitInterval(long limit, long available, long amount);
62
63
64
65
66
67
68
69 public void set(final long limit, final TimeUnit timeUnit) {
70 switch (timeUnit) {
71 case MILLISECONDS:
72 tunit = 1;
73 break;
74 case SECONDS:
75 tunit = 1000;
76 break;
77 case MINUTES:
78 tunit = 60 * 1000;
79 break;
80 case HOURS:
81 tunit = 60 * 60 * 1000;
82 break;
83 case DAYS:
84 tunit = 24 * 60 * 60 * 1000;
85 break;
86 default:
87 throw new RuntimeException("Unsupported " + timeUnit.name() + " TimeUnit.");
88 }
89 this.limit = limit;
90 this.avail = limit;
91 }
92
93 public String toString() {
94 String rateLimiter = this.getClass().getSimpleName();
95 if (limit == Long.MAX_VALUE) {
96 return rateLimiter + "(Bypass)";
97 }
98 return rateLimiter + "(avail=" + avail + " limit=" + limit + " tunit=" + tunit + ")";
99 }
100
101
102
103
104
105
106
107 public synchronized void update(final RateLimiter other) {
108 this.tunit = other.tunit;
109 if (this.limit < other.limit) {
110 this.avail += (other.limit - this.limit);
111 }
112 this.limit = other.limit;
113 }
114
115 public synchronized boolean isBypass() {
116 return limit == Long.MAX_VALUE;
117 }
118
119 public synchronized long getLimit() {
120 return limit;
121 }
122
123 public synchronized long getAvailable() {
124 return avail;
125 }
126
127 protected long getTimeUnitInMillis() {
128 return tunit;
129 }
130
131
132
133
134
135 public boolean canExecute() {
136 return canExecute(1);
137 }
138
139
140
141
142
143
144 public synchronized boolean canExecute(final long amount) {
145 long refillAmount = refill(limit);
146 if (refillAmount == 0 && avail < amount) {
147 return false;
148 }
149
150 if (avail <= Long.MAX_VALUE - refillAmount) {
151 avail = Math.max(0, Math.min(avail + refillAmount, limit));
152 } else {
153 avail = Math.max(0, limit);
154 }
155 if (avail >= amount) {
156 return true;
157 }
158 return false;
159 }
160
161
162
163
164 public void consume() {
165 consume(1);
166 }
167
168
169
170
171
172 public synchronized void consume(final long amount) {
173 this.avail -= amount;
174 if (this.avail < 0) {
175 this.avail = 0;
176 }
177 }
178
179
180
181
182 public long waitInterval() {
183 return waitInterval(1);
184 }
185
186
187
188
189 public synchronized long waitInterval(final long amount) {
190
191 return (amount <= avail) ? 0 : getWaitInterval(limit, avail, amount);
192 }
193
194
195 @VisibleForTesting
196 public abstract void setNextRefillTime(long nextRefillTime);
197
198 @VisibleForTesting
199 public abstract long getNextRefillTime();
200 }