001/** 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018 019package org.apache.hadoop.hbase.quotas; 020 021import java.io.IOException; 022import java.util.HashMap; 023import java.util.List; 024import java.util.Map; 025 026import org.apache.hadoop.conf.Configuration; 027import org.apache.hadoop.hbase.Cell; 028import org.apache.hadoop.hbase.HColumnDescriptor; 029import org.apache.hadoop.hbase.HConstants; 030import org.apache.hadoop.hbase.HTableDescriptor; 031import org.apache.hadoop.hbase.KeyValueUtil; 032import org.apache.hadoop.hbase.TableName; 033import org.apache.yetus.audience.InterfaceAudience; 034import org.apache.yetus.audience.InterfaceStability; 035import org.slf4j.Logger; 036import org.slf4j.LoggerFactory; 037import org.apache.hadoop.hbase.client.Connection; 038import org.apache.hadoop.hbase.client.Delete; 039import org.apache.hadoop.hbase.client.Get; 040import org.apache.hadoop.hbase.client.Mutation; 041import org.apache.hadoop.hbase.client.Put; 042import org.apache.hadoop.hbase.client.Result; 043import org.apache.hadoop.hbase.client.Table; 044import org.apache.hadoop.hbase.shaded.protobuf.generated.QuotaProtos.Quotas; 045import org.apache.hadoop.hbase.regionserver.BloomType; 046import org.apache.hadoop.hbase.util.Bytes; 047import org.apache.hadoop.hbase.util.EnvironmentEdgeManager; 048 049/** 050 * Helper class to interact with the quota table 051 */ 052@InterfaceAudience.Private 053@InterfaceStability.Evolving 054public class QuotaUtil extends QuotaTableUtil { 055 private static final Logger LOG = LoggerFactory.getLogger(QuotaUtil.class); 056 057 public static final String QUOTA_CONF_KEY = "hbase.quota.enabled"; 058 private static final boolean QUOTA_ENABLED_DEFAULT = false; 059 060 /** Table descriptor for Quota internal table */ 061 public static final HTableDescriptor QUOTA_TABLE_DESC = 062 new HTableDescriptor(QUOTA_TABLE_NAME); 063 static { 064 QUOTA_TABLE_DESC.addFamily( 065 new HColumnDescriptor(QUOTA_FAMILY_INFO) 066 .setScope(HConstants.REPLICATION_SCOPE_LOCAL) 067 .setBloomFilterType(BloomType.ROW) 068 .setMaxVersions(1) 069 ); 070 QUOTA_TABLE_DESC.addFamily( 071 new HColumnDescriptor(QUOTA_FAMILY_USAGE) 072 .setScope(HConstants.REPLICATION_SCOPE_LOCAL) 073 .setBloomFilterType(BloomType.ROW) 074 .setMaxVersions(1) 075 ); 076 } 077 078 /** Returns true if the support for quota is enabled */ 079 public static boolean isQuotaEnabled(final Configuration conf) { 080 return conf.getBoolean(QUOTA_CONF_KEY, QUOTA_ENABLED_DEFAULT); 081 } 082 083 /* ========================================================================= 084 * Quota "settings" helpers 085 */ 086 public static void addTableQuota(final Connection connection, final TableName table, 087 final Quotas data) throws IOException { 088 addQuotas(connection, getTableRowKey(table), data); 089 } 090 091 public static void deleteTableQuota(final Connection connection, final TableName table) 092 throws IOException { 093 deleteQuotas(connection, getTableRowKey(table)); 094 } 095 096 public static void addNamespaceQuota(final Connection connection, final String namespace, 097 final Quotas data) throws IOException { 098 addQuotas(connection, getNamespaceRowKey(namespace), data); 099 } 100 101 public static void deleteNamespaceQuota(final Connection connection, final String namespace) 102 throws IOException { 103 deleteQuotas(connection, getNamespaceRowKey(namespace)); 104 } 105 106 public static void addUserQuota(final Connection connection, final String user, 107 final Quotas data) throws IOException { 108 addQuotas(connection, getUserRowKey(user), data); 109 } 110 111 public static void addUserQuota(final Connection connection, final String user, 112 final TableName table, final Quotas data) throws IOException { 113 addQuotas(connection, getUserRowKey(user), getSettingsQualifierForUserTable(table), data); 114 } 115 116 public static void addUserQuota(final Connection connection, final String user, 117 final String namespace, final Quotas data) throws IOException { 118 addQuotas(connection, getUserRowKey(user), 119 getSettingsQualifierForUserNamespace(namespace), data); 120 } 121 122 public static void deleteUserQuota(final Connection connection, final String user) 123 throws IOException { 124 deleteQuotas(connection, getUserRowKey(user)); 125 } 126 127 public static void deleteUserQuota(final Connection connection, final String user, 128 final TableName table) throws IOException { 129 deleteQuotas(connection, getUserRowKey(user), 130 getSettingsQualifierForUserTable(table)); 131 } 132 133 public static void deleteUserQuota(final Connection connection, final String user, 134 final String namespace) throws IOException { 135 deleteQuotas(connection, getUserRowKey(user), 136 getSettingsQualifierForUserNamespace(namespace)); 137 } 138 139 private static void addQuotas(final Connection connection, final byte[] rowKey, 140 final Quotas data) throws IOException { 141 addQuotas(connection, rowKey, QUOTA_QUALIFIER_SETTINGS, data); 142 } 143 144 private static void addQuotas(final Connection connection, final byte[] rowKey, 145 final byte[] qualifier, final Quotas data) throws IOException { 146 Put put = new Put(rowKey); 147 put.addColumn(QUOTA_FAMILY_INFO, qualifier, quotasToData(data)); 148 doPut(connection, put); 149 } 150 151 private static void deleteQuotas(final Connection connection, final byte[] rowKey) 152 throws IOException { 153 deleteQuotas(connection, rowKey, null); 154 } 155 156 private static void deleteQuotas(final Connection connection, final byte[] rowKey, 157 final byte[] qualifier) throws IOException { 158 Delete delete = new Delete(rowKey); 159 if (qualifier != null) { 160 delete.addColumns(QUOTA_FAMILY_INFO, qualifier); 161 } 162 doDelete(connection, delete); 163 } 164 165 public static Map<String, UserQuotaState> fetchUserQuotas(final Connection connection, 166 final List<Get> gets) throws IOException { 167 long nowTs = EnvironmentEdgeManager.currentTime(); 168 Result[] results = doGet(connection, gets); 169 170 Map<String, UserQuotaState> userQuotas = new HashMap<>(results.length); 171 for (int i = 0; i < results.length; ++i) { 172 byte[] key = gets.get(i).getRow(); 173 assert isUserRowKey(key); 174 String user = getUserFromRowKey(key); 175 176 final UserQuotaState quotaInfo = new UserQuotaState(nowTs); 177 userQuotas.put(user, quotaInfo); 178 179 if (results[i].isEmpty()) continue; 180 assert Bytes.equals(key, results[i].getRow()); 181 182 try { 183 parseUserResult(user, results[i], new UserQuotasVisitor() { 184 @Override 185 public void visitUserQuotas(String userName, String namespace, Quotas quotas) { 186 quotaInfo.setQuotas(namespace, quotas); 187 } 188 189 @Override 190 public void visitUserQuotas(String userName, TableName table, Quotas quotas) { 191 quotaInfo.setQuotas(table, quotas); 192 } 193 194 @Override 195 public void visitUserQuotas(String userName, Quotas quotas) { 196 quotaInfo.setQuotas(quotas); 197 } 198 }); 199 } catch (IOException e) { 200 LOG.error("Unable to parse user '" + user + "' quotas", e); 201 userQuotas.remove(user); 202 } 203 } 204 return userQuotas; 205 } 206 207 public static Map<TableName, QuotaState> fetchTableQuotas(final Connection connection, 208 final List<Get> gets) throws IOException { 209 return fetchGlobalQuotas("table", connection, gets, new KeyFromRow<TableName>() { 210 @Override 211 public TableName getKeyFromRow(final byte[] row) { 212 assert isTableRowKey(row); 213 return getTableFromRowKey(row); 214 } 215 }); 216 } 217 218 public static Map<String, QuotaState> fetchNamespaceQuotas(final Connection connection, 219 final List<Get> gets) throws IOException { 220 return fetchGlobalQuotas("namespace", connection, gets, new KeyFromRow<String>() { 221 @Override 222 public String getKeyFromRow(final byte[] row) { 223 assert isNamespaceRowKey(row); 224 return getNamespaceFromRowKey(row); 225 } 226 }); 227 } 228 229 public static <K> Map<K, QuotaState> fetchGlobalQuotas(final String type, 230 final Connection connection, final List<Get> gets, final KeyFromRow<K> kfr) 231 throws IOException { 232 long nowTs = EnvironmentEdgeManager.currentTime(); 233 Result[] results = doGet(connection, gets); 234 235 Map<K, QuotaState> globalQuotas = new HashMap<>(results.length); 236 for (int i = 0; i < results.length; ++i) { 237 byte[] row = gets.get(i).getRow(); 238 K key = kfr.getKeyFromRow(row); 239 240 QuotaState quotaInfo = new QuotaState(nowTs); 241 globalQuotas.put(key, quotaInfo); 242 243 if (results[i].isEmpty()) continue; 244 assert Bytes.equals(row, results[i].getRow()); 245 246 byte[] data = results[i].getValue(QUOTA_FAMILY_INFO, QUOTA_QUALIFIER_SETTINGS); 247 if (data == null) continue; 248 249 try { 250 Quotas quotas = quotasFromData(data); 251 quotaInfo.setQuotas(quotas); 252 } catch (IOException e) { 253 LOG.error("Unable to parse " + type + " '" + key + "' quotas", e); 254 globalQuotas.remove(key); 255 } 256 } 257 return globalQuotas; 258 } 259 260 private static interface KeyFromRow<T> { 261 T getKeyFromRow(final byte[] row); 262 } 263 264 /* ========================================================================= 265 * HTable helpers 266 */ 267 private static void doPut(final Connection connection, final Put put) 268 throws IOException { 269 try (Table table = connection.getTable(QuotaUtil.QUOTA_TABLE_NAME)) { 270 table.put(put); 271 } 272 } 273 274 private static void doDelete(final Connection connection, final Delete delete) 275 throws IOException { 276 try (Table table = connection.getTable(QuotaUtil.QUOTA_TABLE_NAME)) { 277 table.delete(delete); 278 } 279 } 280 281 /* ========================================================================= 282 * Data Size Helpers 283 */ 284 public static long calculateMutationSize(final Mutation mutation) { 285 long size = 0; 286 for (Map.Entry<byte [], List<Cell>> entry : mutation.getFamilyCellMap().entrySet()) { 287 for (Cell cell : entry.getValue()) { 288 size += KeyValueUtil.length(cell); 289 } 290 } 291 return size; 292 } 293 294 public static long calculateResultSize(final Result result) { 295 long size = 0; 296 for (Cell cell : result.rawCells()) { 297 size += KeyValueUtil.length(cell); 298 } 299 return size; 300 } 301 302 public static long calculateResultSize(final List<Result> results) { 303 long size = 0; 304 for (Result result: results) { 305 for (Cell cell : result.rawCells()) { 306 size += KeyValueUtil.length(cell); 307 } 308 } 309 return size; 310 } 311}