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
54 public static final HTableDescriptor QUOTA_TABLE_DESC = new HTableDescriptor(QUOTA_TABLE_NAME);
55 static {
56 QUOTA_TABLE_DESC.addFamily(new HColumnDescriptor(QUOTA_FAMILY_INFO)
57 .setScope(HConstants.REPLICATION_SCOPE_LOCAL).setBloomFilterType(BloomType.ROW)
58 .setMaxVersions(1));
59 QUOTA_TABLE_DESC.addFamily(new HColumnDescriptor(QUOTA_FAMILY_USAGE)
60 .setScope(HConstants.REPLICATION_SCOPE_LOCAL).setBloomFilterType(BloomType.ROW)
61 .setMaxVersions(1));
62 }
63
64
65 public static boolean isQuotaEnabled(final Configuration conf) {
66 return conf.getBoolean(QUOTA_CONF_KEY, QUOTA_ENABLED_DEFAULT);
67 }
68
69
70
71
72
73 public static void addTableQuota(final Connection connection, final TableName table,
74 final Quotas data) throws IOException {
75 addQuotas(connection, getTableRowKey(table), data);
76 }
77
78 public static void deleteTableQuota(final Connection connection, final TableName table)
79 throws IOException {
80 deleteQuotas(connection, getTableRowKey(table));
81 }
82
83 public static void addNamespaceQuota(final Connection connection, final String namespace,
84 final Quotas data) throws IOException {
85 addQuotas(connection, getNamespaceRowKey(namespace), data);
86 }
87
88 public static void deleteNamespaceQuota(final Connection connection, final String namespace)
89 throws IOException {
90 deleteQuotas(connection, getNamespaceRowKey(namespace));
91 }
92
93 public static void
94 addUserQuota(final Connection connection, final String user, final Quotas data)
95 throws IOException {
96 addQuotas(connection, getUserRowKey(user), data);
97 }
98
99 public static void addUserQuota(final Connection connection, final String user,
100 final TableName table, final Quotas data) throws IOException {
101 addQuotas(connection, getUserRowKey(user), getSettingsQualifierForUserTable(table), data);
102 }
103
104 public static void addUserQuota(final Connection connection, final String user,
105 final String namespace, final Quotas data) throws IOException {
106 addQuotas(connection, getUserRowKey(user), getSettingsQualifierForUserNamespace(namespace),
107 data);
108 }
109
110 public static void deleteUserQuota(final Connection connection, final String user)
111 throws IOException {
112 deleteQuotas(connection, getUserRowKey(user));
113 }
114
115 public static void deleteUserQuota(final Connection connection, final String user,
116 final TableName table) throws IOException {
117 deleteQuotas(connection, getUserRowKey(user), getSettingsQualifierForUserTable(table));
118 }
119
120 public static void deleteUserQuota(final Connection connection, final String user,
121 final String namespace) throws IOException {
122 deleteQuotas(connection, getUserRowKey(user), getSettingsQualifierForUserNamespace(namespace));
123 }
124
125 private static void
126 addQuotas(final Connection connection, final byte[] rowKey, final Quotas data)
127 throws IOException {
128 addQuotas(connection, rowKey, QUOTA_QUALIFIER_SETTINGS, data);
129 }
130
131 private static void addQuotas(final Connection connection, final byte[] rowKey,
132 final byte[] qualifier, final Quotas data) throws IOException {
133 Put put = new Put(rowKey);
134 put.addColumn(QUOTA_FAMILY_INFO, qualifier, quotasToData(data));
135 doPut(connection, put);
136 }
137
138 private static void deleteQuotas(final Connection connection, final byte[] rowKey)
139 throws IOException {
140 deleteQuotas(connection, rowKey, null);
141 }
142
143 private static void deleteQuotas(final Connection connection, final byte[] rowKey,
144 final byte[] qualifier) throws IOException {
145 Delete delete = new Delete(rowKey);
146 if (qualifier != null) {
147 delete.addColumns(QUOTA_FAMILY_INFO, qualifier);
148 }
149 doDelete(connection, delete);
150 }
151
152 public static Map<String, UserQuotaState> fetchUserQuotas(final Connection connection,
153 final List<Get> gets) throws IOException {
154 long nowTs = EnvironmentEdgeManager.currentTime();
155 Result[] results = doGet(connection, gets);
156
157 Map<String, UserQuotaState> userQuotas = new HashMap<String, UserQuotaState>(results.length);
158 for (int i = 0; i < results.length; ++i) {
159 byte[] key = gets.get(i).getRow();
160 assert isUserRowKey(key);
161 String user = getUserFromRowKey(key);
162
163 final UserQuotaState quotaInfo = new UserQuotaState(nowTs);
164 userQuotas.put(user, quotaInfo);
165
166 if (results[i].isEmpty()) continue;
167 assert Bytes.equals(key, results[i].getRow());
168
169 try {
170 parseUserResult(user, results[i], new UserQuotasVisitor() {
171 @Override
172 public void visitUserQuotas(String userName, String namespace, Quotas quotas) {
173 quotaInfo.setQuotas(namespace, quotas);
174 }
175
176 @Override
177 public void visitUserQuotas(String userName, TableName table, Quotas quotas) {
178 quotaInfo.setQuotas(table, quotas);
179 }
180
181 @Override
182 public void visitUserQuotas(String userName, Quotas quotas) {
183 quotaInfo.setQuotas(quotas);
184 }
185 });
186 } catch (IOException e) {
187 LOG.error("Unable to parse user '" + user + "' quotas", e);
188 userQuotas.remove(user);
189 }
190 }
191 return userQuotas;
192 }
193
194 public static Map<TableName, QuotaState> fetchTableQuotas(final Connection connection,
195 final List<Get> gets) throws IOException {
196 return fetchGlobalQuotas("table", connection, gets, new KeyFromRow<TableName>() {
197 @Override
198 public TableName getKeyFromRow(final byte[] row) {
199 assert isTableRowKey(row);
200 return getTableFromRowKey(row);
201 }
202 });
203 }
204
205 public static Map<String, QuotaState> fetchNamespaceQuotas(final Connection connection,
206 final List<Get> gets) throws IOException {
207 return fetchGlobalQuotas("namespace", connection, gets, new KeyFromRow<String>() {
208 @Override
209 public String getKeyFromRow(final byte[] row) {
210 assert isNamespaceRowKey(row);
211 return getNamespaceFromRowKey(row);
212 }
213 });
214 }
215
216 public static <K> Map<K, QuotaState> fetchGlobalQuotas(final String type,
217 final Connection connection, final List<Get> gets, final KeyFromRow<K> kfr)
218 throws IOException {
219 long nowTs = EnvironmentEdgeManager.currentTime();
220 Result[] results = doGet(connection, gets);
221
222 Map<K, QuotaState> globalQuotas = new HashMap<K, QuotaState>(results.length);
223 for (int i = 0; i < results.length; ++i) {
224 byte[] row = gets.get(i).getRow();
225 K key = kfr.getKeyFromRow(row);
226
227 QuotaState quotaInfo = new QuotaState(nowTs);
228 globalQuotas.put(key, quotaInfo);
229
230 if (results[i].isEmpty()) continue;
231 assert Bytes.equals(row, results[i].getRow());
232
233 byte[] data = results[i].getValue(QUOTA_FAMILY_INFO, QUOTA_QUALIFIER_SETTINGS);
234 if (data == null) continue;
235
236 try {
237 Quotas quotas = quotasFromData(data);
238 quotaInfo.setQuotas(quotas);
239 } catch (IOException e) {
240 LOG.error("Unable to parse " + type + " '" + key + "' quotas", e);
241 globalQuotas.remove(key);
242 }
243 }
244 return globalQuotas;
245 }
246
247 private static interface KeyFromRow<T> {
248 T getKeyFromRow(final byte[] row);
249 }
250
251
252
253
254 private static void doPut(final Connection connection, final Put put) throws IOException {
255 try (Table table = connection.getTable(QuotaUtil.QUOTA_TABLE_NAME)) {
256 table.put(put);
257 }
258 }
259
260 private static void doDelete(final Connection connection, final Delete delete)
261 throws IOException {
262 try (Table table = connection.getTable(QuotaUtil.QUOTA_TABLE_NAME)) {
263 table.delete(delete);
264 }
265 }
266
267
268
269
270 public static long calculateMutationSize(final Mutation mutation) {
271 long size = 0;
272 for (Map.Entry<byte[], List<Cell>> entry : mutation.getFamilyCellMap().entrySet()) {
273 for (Cell cell : entry.getValue()) {
274 size += KeyValueUtil.length(cell);
275 }
276 }
277 return size;
278 }
279
280 public static long calculateResultSize(final Result result) {
281 long size = 0;
282 for (Cell cell : result.rawCells()) {
283 size += KeyValueUtil.length(cell);
284 }
285 return size;
286 }
287
288 public static long calculateResultSize(final List<Result> results) {
289 long size = 0;
290 for (Result result : results) {
291 for (Cell cell : result.rawCells()) {
292 size += KeyValueUtil.length(cell);
293 }
294 }
295 return size;
296 }
297 }