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.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   * Helper class to interact with the quota table
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    /** Table descriptor for Quota internal table */
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    /** Returns true if the support for quota is enabled */
65    public static boolean isQuotaEnabled(final Configuration conf) {
66      return conf.getBoolean(QUOTA_CONF_KEY, QUOTA_ENABLED_DEFAULT);
67    }
68  
69    /*
70     * ========================================================================= Quota "settings"
71     * helpers
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    * ========================================================================= HTable helpers
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    * ========================================================================= Data Size Helpers
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 }