1
2
3
4
5
6
7
8
9
10
11
12 package org.apache.hadoop.hbase.quotas;
13
14 import java.io.IOException;
15 import java.util.HashMap;
16 import java.util.List;
17 import java.util.Map;
18
19 import org.apache.commons.logging.Log;
20 import org.apache.commons.logging.LogFactory;
21 import org.apache.hadoop.conf.Configuration;
22 import org.apache.hadoop.hbase.Cell;
23 import org.apache.hadoop.hbase.HColumnDescriptor;
24 import org.apache.hadoop.hbase.HConstants;
25 import org.apache.hadoop.hbase.HTableDescriptor;
26 import org.apache.hadoop.hbase.KeyValueUtil;
27 import org.apache.hadoop.hbase.TableName;
28 import org.apache.hadoop.hbase.classification.InterfaceAudience;
29 import org.apache.hadoop.hbase.classification.InterfaceStability;
30 import org.apache.hadoop.hbase.client.Connection;
31 import org.apache.hadoop.hbase.client.Delete;
32 import org.apache.hadoop.hbase.client.Get;
33 import org.apache.hadoop.hbase.client.Mutation;
34 import org.apache.hadoop.hbase.client.Put;
35 import org.apache.hadoop.hbase.client.Result;
36 import org.apache.hadoop.hbase.client.Table;
37 import org.apache.hadoop.hbase.protobuf.generated.QuotaProtos.Quotas;
38 import org.apache.hadoop.hbase.regionserver.BloomType;
39 import org.apache.hadoop.hbase.util.Bytes;
40 import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
41
42
43
44
45 @InterfaceAudience.Private
46 @InterfaceStability.Evolving
47 public class QuotaUtil extends QuotaTableUtil {
48 private static final Log LOG = LogFactory.getLog(QuotaUtil.class);
49
50 public static final String QUOTA_CONF_KEY = "hbase.quota.enabled";
51 private static final boolean QUOTA_ENABLED_DEFAULT = false;
52
53 public static final String QUOTA_RETRYABLE_THROTTING_EXCEPTION_CONF_KEY =
54 "hbase.quota.retryable.throttlingexception";
55 public static final boolean QUOTA_RETRYABLE_THROTTING_EXCEPTION_DEFAULT = false;
56
57 public static final String READ_CAPACITY_UNIT_CONF_KEY = "hbase.quota.read.capacity.unit";
58
59 public static final long DEFAULT_READ_CAPACITY_UNIT = 1024;
60 public static final String WRITE_CAPACITY_UNIT_CONF_KEY = "hbase.quota.write.capacity.unit";
61
62 public static final long DEFAULT_WRITE_CAPACITY_UNIT = 1024;
63
64
65 public static final HTableDescriptor QUOTA_TABLE_DESC = new HTableDescriptor(QUOTA_TABLE_NAME);
66 static {
67 QUOTA_TABLE_DESC.addFamily(new HColumnDescriptor(QUOTA_FAMILY_INFO)
68 .setScope(HConstants.REPLICATION_SCOPE_LOCAL).setBloomFilterType(BloomType.ROW)
69 .setMaxVersions(1));
70 QUOTA_TABLE_DESC.addFamily(new HColumnDescriptor(QUOTA_FAMILY_USAGE)
71 .setScope(HConstants.REPLICATION_SCOPE_LOCAL).setBloomFilterType(BloomType.ROW)
72 .setMaxVersions(1));
73 }
74
75
76 public static boolean isQuotaEnabled(final Configuration conf) {
77 return conf.getBoolean(QUOTA_CONF_KEY, QUOTA_ENABLED_DEFAULT);
78 }
79
80
81
82
83
84 public static void addTableQuota(final Connection connection, final TableName table,
85 final Quotas data) throws IOException {
86 addQuotas(connection, getTableRowKey(table), data);
87 }
88
89 public static void deleteTableQuota(final Connection connection, final TableName table)
90 throws IOException {
91 deleteQuotas(connection, getTableRowKey(table));
92 }
93
94 public static void addNamespaceQuota(final Connection connection, final String namespace,
95 final Quotas data) throws IOException {
96 addQuotas(connection, getNamespaceRowKey(namespace), data);
97 }
98
99 public static void deleteNamespaceQuota(final Connection connection, final String namespace)
100 throws IOException {
101 deleteQuotas(connection, getNamespaceRowKey(namespace));
102 }
103
104 public static void
105 addUserQuota(final Connection connection, final String user, final Quotas data)
106 throws IOException {
107 addQuotas(connection, getUserRowKey(user), data);
108 }
109
110 public static void addUserQuota(final Connection connection, final String user,
111 final TableName table, final Quotas data) throws IOException {
112 addQuotas(connection, getUserRowKey(user), getSettingsQualifierForUserTable(table), data);
113 }
114
115 public static void addUserQuota(final Connection connection, final String user,
116 final String namespace, final Quotas data) throws IOException {
117 addQuotas(connection, getUserRowKey(user), getSettingsQualifierForUserNamespace(namespace),
118 data);
119 }
120
121 public static void deleteUserQuota(final Connection connection, final String user)
122 throws IOException {
123 deleteQuotas(connection, getUserRowKey(user));
124 }
125
126 public static void deleteUserQuota(final Connection connection, final String user,
127 final TableName table) throws IOException {
128 deleteQuotas(connection, getUserRowKey(user), getSettingsQualifierForUserTable(table));
129 }
130
131 public static void deleteUserQuota(final Connection connection, final String user,
132 final String namespace) throws IOException {
133 deleteQuotas(connection, getUserRowKey(user), getSettingsQualifierForUserNamespace(namespace));
134 }
135
136 private static void
137 addQuotas(final Connection connection, final byte[] rowKey, final Quotas data)
138 throws IOException {
139 addQuotas(connection, rowKey, QUOTA_QUALIFIER_SETTINGS, data);
140 }
141
142 private static void addQuotas(final Connection connection, final byte[] rowKey,
143 final byte[] qualifier, final Quotas data) throws IOException {
144 Put put = new Put(rowKey);
145 put.addColumn(QUOTA_FAMILY_INFO, qualifier, quotasToData(data));
146 doPut(connection, put);
147 }
148
149 private static void deleteQuotas(final Connection connection, final byte[] rowKey)
150 throws IOException {
151 deleteQuotas(connection, rowKey, null);
152 }
153
154 private static void deleteQuotas(final Connection connection, final byte[] rowKey,
155 final byte[] qualifier) throws IOException {
156 Delete delete = new Delete(rowKey);
157 if (qualifier != null) {
158 delete.addColumns(QUOTA_FAMILY_INFO, qualifier);
159 }
160 doDelete(connection, delete);
161 }
162
163 public static Map<String, UserQuotaState> fetchUserQuotas(final Connection connection,
164 final List<Get> gets) throws IOException {
165 long nowTs = EnvironmentEdgeManager.currentTime();
166 Result[] results = doGet(connection, gets);
167
168 Map<String, UserQuotaState> userQuotas = new HashMap<String, UserQuotaState>(results.length);
169 for (int i = 0; i < results.length; ++i) {
170 byte[] key = gets.get(i).getRow();
171 assert isUserRowKey(key);
172 String user = getUserFromRowKey(key);
173
174 final UserQuotaState quotaInfo = new UserQuotaState(nowTs);
175 userQuotas.put(user, quotaInfo);
176
177 if (results[i].isEmpty()) continue;
178 assert Bytes.equals(key, results[i].getRow());
179
180 try {
181 parseUserResult(user, results[i], new UserQuotasVisitor() {
182 @Override
183 public void visitUserQuotas(String userName, String namespace, Quotas quotas) {
184 quotaInfo.setQuotas(namespace, quotas);
185 }
186
187 @Override
188 public void visitUserQuotas(String userName, TableName table, Quotas quotas) {
189 quotaInfo.setQuotas(table, quotas);
190 }
191
192 @Override
193 public void visitUserQuotas(String userName, Quotas quotas) {
194 quotaInfo.setQuotas(quotas);
195 }
196 });
197 } catch (IOException e) {
198 LOG.error("Unable to parse user '" + user + "' quotas", e);
199 userQuotas.remove(user);
200 }
201 }
202 return userQuotas;
203 }
204
205 public static Map<TableName, QuotaState> fetchTableQuotas(final Connection connection,
206 final List<Get> gets) throws IOException {
207 return fetchGlobalQuotas("table", connection, gets, new KeyFromRow<TableName>() {
208 @Override
209 public TableName getKeyFromRow(final byte[] row) {
210 assert isTableRowKey(row);
211 return getTableFromRowKey(row);
212 }
213 });
214 }
215
216 public static Map<String, QuotaState> fetchNamespaceQuotas(final Connection connection,
217 final List<Get> gets) throws IOException {
218 return fetchGlobalQuotas("namespace", connection, gets, new KeyFromRow<String>() {
219 @Override
220 public String getKeyFromRow(final byte[] row) {
221 assert isNamespaceRowKey(row);
222 return getNamespaceFromRowKey(row);
223 }
224 });
225 }
226
227 public static <K> Map<K, QuotaState> fetchGlobalQuotas(final String type,
228 final Connection connection, final List<Get> gets, final KeyFromRow<K> kfr)
229 throws IOException {
230 long nowTs = EnvironmentEdgeManager.currentTime();
231 Result[] results = doGet(connection, gets);
232
233 Map<K, QuotaState> globalQuotas = new HashMap<K, QuotaState>(results.length);
234 for (int i = 0; i < results.length; ++i) {
235 byte[] row = gets.get(i).getRow();
236 K key = kfr.getKeyFromRow(row);
237
238 QuotaState quotaInfo = new QuotaState(nowTs);
239 globalQuotas.put(key, quotaInfo);
240
241 if (results[i].isEmpty()) continue;
242 assert Bytes.equals(row, results[i].getRow());
243
244 byte[] data = results[i].getValue(QUOTA_FAMILY_INFO, QUOTA_QUALIFIER_SETTINGS);
245 if (data == null) continue;
246
247 try {
248 Quotas quotas = quotasFromData(data);
249 quotaInfo.setQuotas(quotas);
250 } catch (IOException e) {
251 LOG.error("Unable to parse " + type + " '" + key + "' quotas", e);
252 globalQuotas.remove(key);
253 }
254 }
255 return globalQuotas;
256 }
257
258 private static interface KeyFromRow<T> {
259 T getKeyFromRow(final byte[] row);
260 }
261
262
263
264
265 private static void doPut(final Connection connection, final Put put) throws IOException {
266 try (Table table = connection.getTable(QuotaUtil.QUOTA_TABLE_NAME)) {
267 table.put(put);
268 }
269 }
270
271 private static void doDelete(final Connection connection, final Delete delete)
272 throws IOException {
273 try (Table table = connection.getTable(QuotaUtil.QUOTA_TABLE_NAME)) {
274 table.delete(delete);
275 }
276 }
277
278
279
280
281 public static long calculateMutationSize(final Mutation mutation) {
282 long size = 0;
283 for (Map.Entry<byte[], List<Cell>> entry : mutation.getFamilyCellMap().entrySet()) {
284 for (Cell cell : entry.getValue()) {
285 size += KeyValueUtil.length(cell);
286 }
287 }
288 return size;
289 }
290
291 public static long calculateResultSize(final Result result) {
292 long size = 0;
293 for (Cell cell : result.rawCells()) {
294 size += KeyValueUtil.length(cell);
295 }
296 return size;
297 }
298
299 public static long calculateResultSize(final List<Result> results) {
300 long size = 0;
301 for (Result result : results) {
302 for (Cell cell : result.rawCells()) {
303 size += KeyValueUtil.length(cell);
304 }
305 }
306 return size;
307 }
308 }