All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.hadoop.hbase.quotas.QuotaTableUtil Maven / Gradle / Ivy

There is a newer version: 3.0.0-beta-1
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.hadoop.hbase.quotas;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.regex.Pattern;
import org.apache.commons.lang3.StringUtils;
import org.apache.hadoop.hbase.Cell;
import org.apache.hadoop.hbase.CellScanner;
import org.apache.hadoop.hbase.CompareOperator;
import org.apache.hadoop.hbase.NamespaceDescriptor;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.client.Connection;
import org.apache.hadoop.hbase.client.Delete;
import org.apache.hadoop.hbase.client.Get;
import org.apache.hadoop.hbase.client.Put;
import org.apache.hadoop.hbase.client.Result;
import org.apache.hadoop.hbase.client.ResultScanner;
import org.apache.hadoop.hbase.client.Scan;
import org.apache.hadoop.hbase.client.Table;
import org.apache.hadoop.hbase.filter.ColumnPrefixFilter;
import org.apache.hadoop.hbase.filter.Filter;
import org.apache.hadoop.hbase.filter.FilterList;
import org.apache.hadoop.hbase.filter.QualifierFilter;
import org.apache.hadoop.hbase.filter.RegexStringComparator;
import org.apache.hadoop.hbase.filter.RowFilter;
import org.apache.hadoop.hbase.protobuf.ProtobufMagic;
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.yetus.audience.InterfaceAudience;
import org.apache.yetus.audience.InterfaceStability;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.apache.hbase.thirdparty.com.google.common.collect.HashMultimap;
import org.apache.hbase.thirdparty.com.google.common.collect.Multimap;
import org.apache.hbase.thirdparty.com.google.protobuf.ByteString;
import org.apache.hbase.thirdparty.com.google.protobuf.InvalidProtocolBufferException;
import org.apache.hbase.thirdparty.com.google.protobuf.UnsafeByteOperations;

import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil;
import org.apache.hadoop.hbase.shaded.protobuf.generated.QuotaProtos;
import org.apache.hadoop.hbase.shaded.protobuf.generated.QuotaProtos.Quotas;
import org.apache.hadoop.hbase.shaded.protobuf.generated.QuotaProtos.SpaceQuota;

/**
 * Helper class to interact with the quota table.
 * 
 * 
 * 
 * 
 * 
 * 
 * 
 * 
 * 
 * 
 * 
 * 
 * 
 * 
 * 
 * 
 * 
 * 
 * 
 * 
 * 
 * 
 * 
 * 
 * 
 * 
 * 
 * 
 * 
 * 
 * 
 * 
 * 
 * 
 * 
 * 
 * 
 * 
 * 
 * 
 * 
 * 
 * 
 * 
 * 
 * 
 * 
 * 
 * 
 * 
 * 
 * 
 * 
 * 
 * 
ROW-KEYFAM/QUALDATADESC
n.<namespace>q:s<global-quotas>
n.<namespace>u:p<namespace-quota policy>
n.<namespace>u:s<SpaceQuotaSnapshot>The size of all snapshots against tables in the namespace
t.<table>q:s<global-quotas>
t.<table>u:p<table-quota policy>
t.<table>u:ss.<snapshot name><SpaceQuotaSnapshot>The size of a snapshot against a table
u.<user>q:s<global-quotas>
u.<user>q:s.<table><table-quotas>
u.<user>q:s.<ns><namespace-quotas>
*/ @InterfaceAudience.Private @InterfaceStability.Evolving public class QuotaTableUtil { private static final Logger LOG = LoggerFactory.getLogger(QuotaTableUtil.class); /** System table for quotas */ public static final TableName QUOTA_TABLE_NAME = TableName.valueOf(NamespaceDescriptor.SYSTEM_NAMESPACE_NAME_STR, "quota"); protected static final byte[] QUOTA_FAMILY_INFO = Bytes.toBytes("q"); protected static final byte[] QUOTA_FAMILY_USAGE = Bytes.toBytes("u"); protected static final byte[] QUOTA_QUALIFIER_SETTINGS = Bytes.toBytes("s"); protected static final byte[] QUOTA_QUALIFIER_SETTINGS_PREFIX = Bytes.toBytes("s."); protected static final byte[] QUOTA_QUALIFIER_POLICY = Bytes.toBytes("p"); protected static final byte[] QUOTA_SNAPSHOT_SIZE_QUALIFIER = Bytes.toBytes("ss"); protected static final String QUOTA_POLICY_COLUMN = Bytes.toString(QUOTA_FAMILY_USAGE) + ":" + Bytes.toString(QUOTA_QUALIFIER_POLICY); protected static final byte[] QUOTA_USER_ROW_KEY_PREFIX = Bytes.toBytes("u."); protected static final byte[] QUOTA_TABLE_ROW_KEY_PREFIX = Bytes.toBytes("t."); protected static final byte[] QUOTA_NAMESPACE_ROW_KEY_PREFIX = Bytes.toBytes("n."); protected static final byte[] QUOTA_REGION_SERVER_ROW_KEY_PREFIX = Bytes.toBytes("r."); private static final byte[] QUOTA_EXCEED_THROTTLE_QUOTA_ROW_KEY = Bytes.toBytes("exceedThrottleQuota"); /* * TODO: Setting specified region server quota isn't supported currently and the row key "r.all" * represents the throttle quota of all region servers */ public static final String QUOTA_REGION_SERVER_ROW_KEY = "all"; /* * ========================================================================= Quota "settings" * helpers */ public static Quotas getTableQuota(final Connection connection, final TableName table) throws IOException { return getQuotas(connection, getTableRowKey(table)); } public static Quotas getNamespaceQuota(final Connection connection, final String namespace) throws IOException { return getQuotas(connection, getNamespaceRowKey(namespace)); } public static Quotas getUserQuota(final Connection connection, final String user) throws IOException { return getQuotas(connection, getUserRowKey(user)); } public static Quotas getUserQuota(final Connection connection, final String user, final TableName table) throws IOException { return getQuotas(connection, getUserRowKey(user), getSettingsQualifierForUserTable(table)); } public static Quotas getUserQuota(final Connection connection, final String user, final String namespace) throws IOException { return getQuotas(connection, getUserRowKey(user), getSettingsQualifierForUserNamespace(namespace)); } private static Quotas getQuotas(final Connection connection, final byte[] rowKey) throws IOException { return getQuotas(connection, rowKey, QUOTA_QUALIFIER_SETTINGS); } public static Quotas getRegionServerQuota(final Connection connection, final String regionServer) throws IOException { return getQuotas(connection, getRegionServerRowKey(regionServer)); } private static Quotas getQuotas(final Connection connection, final byte[] rowKey, final byte[] qualifier) throws IOException { Get get = new Get(rowKey); get.addColumn(QUOTA_FAMILY_INFO, qualifier); Result result = doGet(connection, get); if (result.isEmpty()) { return null; } return quotasFromData(result.getValue(QUOTA_FAMILY_INFO, qualifier)); } public static Get makeGetForTableQuotas(final TableName table) { Get get = new Get(getTableRowKey(table)); get.addFamily(QUOTA_FAMILY_INFO); return get; } public static Get makeGetForNamespaceQuotas(final String namespace) { Get get = new Get(getNamespaceRowKey(namespace)); get.addFamily(QUOTA_FAMILY_INFO); return get; } public static Get makeGetForRegionServerQuotas(final String regionServer) { Get get = new Get(getRegionServerRowKey(regionServer)); get.addFamily(QUOTA_FAMILY_INFO); return get; } public static Get makeGetForUserQuotas(final String user, final Iterable tables, final Iterable namespaces) { Get get = new Get(getUserRowKey(user)); get.addColumn(QUOTA_FAMILY_INFO, QUOTA_QUALIFIER_SETTINGS); for (final TableName table : tables) { get.addColumn(QUOTA_FAMILY_INFO, getSettingsQualifierForUserTable(table)); } for (final String ns : namespaces) { get.addColumn(QUOTA_FAMILY_INFO, getSettingsQualifierForUserNamespace(ns)); } return get; } public static Scan makeScan(final QuotaFilter filter) { Scan scan = new Scan(); scan.addFamily(QUOTA_FAMILY_INFO); if (filter != null && !filter.isNull()) { scan.setFilter(makeFilter(filter)); } return scan; } /** * converts quotafilter to serializeable filterlists. */ public static Filter makeFilter(final QuotaFilter filter) { FilterList filterList = new FilterList(FilterList.Operator.MUST_PASS_ALL); if (StringUtils.isNotEmpty(filter.getUserFilter())) { FilterList userFilters = new FilterList(FilterList.Operator.MUST_PASS_ONE); boolean hasFilter = false; if (StringUtils.isNotEmpty(filter.getNamespaceFilter())) { FilterList nsFilters = new FilterList(FilterList.Operator.MUST_PASS_ALL); nsFilters.addFilter(new RowFilter(CompareOperator.EQUAL, new RegexStringComparator(getUserRowKeyRegex(filter.getUserFilter()), 0))); nsFilters.addFilter(new QualifierFilter(CompareOperator.EQUAL, new RegexStringComparator( getSettingsQualifierRegexForUserNamespace(filter.getNamespaceFilter()), 0))); userFilters.addFilter(nsFilters); hasFilter = true; } if (StringUtils.isNotEmpty(filter.getTableFilter())) { FilterList tableFilters = new FilterList(FilterList.Operator.MUST_PASS_ALL); tableFilters.addFilter(new RowFilter(CompareOperator.EQUAL, new RegexStringComparator(getUserRowKeyRegex(filter.getUserFilter()), 0))); tableFilters.addFilter(new QualifierFilter(CompareOperator.EQUAL, new RegexStringComparator( getSettingsQualifierRegexForUserTable(filter.getTableFilter()), 0))); userFilters.addFilter(tableFilters); hasFilter = true; } if (!hasFilter) { userFilters.addFilter(new RowFilter(CompareOperator.EQUAL, new RegexStringComparator(getUserRowKeyRegex(filter.getUserFilter()), 0))); } filterList.addFilter(userFilters); } else if (StringUtils.isNotEmpty(filter.getTableFilter())) { filterList.addFilter(new RowFilter(CompareOperator.EQUAL, new RegexStringComparator(getTableRowKeyRegex(filter.getTableFilter()), 0))); } else if (StringUtils.isNotEmpty(filter.getNamespaceFilter())) { filterList.addFilter(new RowFilter(CompareOperator.EQUAL, new RegexStringComparator(getNamespaceRowKeyRegex(filter.getNamespaceFilter()), 0))); } else if (StringUtils.isNotEmpty(filter.getRegionServerFilter())) { filterList.addFilter(new RowFilter(CompareOperator.EQUAL, new RegexStringComparator(getRegionServerRowKeyRegex(filter.getRegionServerFilter()), 0))); } return filterList; } /** * Creates a {@link Scan} which returns only quota snapshots from the quota table. */ public static Scan makeQuotaSnapshotScan() { return makeQuotaSnapshotScanForTable(null); } /** * Fetches all {@link SpaceQuotaSnapshot} objects from the {@code hbase:quota} table. * @param conn The HBase connection * @return A map of table names and their computed snapshot. */ public static Map getSnapshots(Connection conn) throws IOException { Map snapshots = new HashMap<>(); try (Table quotaTable = conn.getTable(QUOTA_TABLE_NAME); ResultScanner rs = quotaTable.getScanner(makeQuotaSnapshotScan())) { for (Result r : rs) { extractQuotaSnapshot(r, snapshots); } } return snapshots; } /** * Creates a {@link Scan} which returns only {@link SpaceQuotaSnapshot} from the quota table for a * specific table. * @param tn Optionally, a table name to limit the scan's rowkey space. Can be null. */ public static Scan makeQuotaSnapshotScanForTable(TableName tn) { Scan s = new Scan(); // Limit to "u:v" column s.addColumn(QUOTA_FAMILY_USAGE, QUOTA_QUALIFIER_POLICY); if (null == tn) { s.setStartStopRowForPrefixScan(QUOTA_TABLE_ROW_KEY_PREFIX); } else { byte[] row = getTableRowKey(tn); // Limit rowspace to the "t:" prefix s.withStartRow(row, true).withStopRow(row, true); } return s; } /** * Creates a {@link Get} which returns only {@link SpaceQuotaSnapshot} from the quota table for a * specific table. * @param tn table name to get from. Can't be null. */ public static Get makeQuotaSnapshotGetForTable(TableName tn) { Get g = new Get(getTableRowKey(tn)); // Limit to "u:v" column g.addColumn(QUOTA_FAMILY_USAGE, QUOTA_QUALIFIER_POLICY); return g; } /** * Extracts the {@link SpaceViolationPolicy} and {@link TableName} from the provided * {@link Result} and adds them to the given {@link Map}. If the result does not contain the * expected information or the serialized policy in the value is invalid, this method will throw * an {@link IllegalArgumentException}. * @param result A row from the quota table. * @param snapshots A map of snapshots to add the result of this method into. */ public static void extractQuotaSnapshot(Result result, Map snapshots) { byte[] row = Objects.requireNonNull(result).getRow(); if (row == null || row.length == 0) { throw new IllegalArgumentException("Provided result had a null row"); } final TableName targetTableName = getTableFromRowKey(row); Cell c = result.getColumnLatestCell(QUOTA_FAMILY_USAGE, QUOTA_QUALIFIER_POLICY); if (c == null) { throw new IllegalArgumentException("Result did not contain the expected column " + QUOTA_POLICY_COLUMN + ", " + result.toString()); } ByteString buffer = UnsafeByteOperations.unsafeWrap(c.getValueArray(), c.getValueOffset(), c.getValueLength()); try { QuotaProtos.SpaceQuotaSnapshot snapshot = QuotaProtos.SpaceQuotaSnapshot.parseFrom(buffer); snapshots.put(targetTableName, SpaceQuotaSnapshot.toSpaceQuotaSnapshot(snapshot)); } catch (InvalidProtocolBufferException e) { throw new IllegalArgumentException( "Result did not contain a valid SpaceQuota protocol buffer message", e); } } public static interface UserQuotasVisitor { void visitUserQuotas(final String userName, final Quotas quotas) throws IOException; void visitUserQuotas(final String userName, final TableName table, final Quotas quotas) throws IOException; void visitUserQuotas(final String userName, final String namespace, final Quotas quotas) throws IOException; } public static interface TableQuotasVisitor { void visitTableQuotas(final TableName tableName, final Quotas quotas) throws IOException; } public static interface NamespaceQuotasVisitor { void visitNamespaceQuotas(final String namespace, final Quotas quotas) throws IOException; } private static interface RegionServerQuotasVisitor { void visitRegionServerQuotas(final String regionServer, final Quotas quotas) throws IOException; } public static interface QuotasVisitor extends UserQuotasVisitor, TableQuotasVisitor, NamespaceQuotasVisitor, RegionServerQuotasVisitor { } public static void parseResult(final Result result, final QuotasVisitor visitor) throws IOException { byte[] row = result.getRow(); if (isNamespaceRowKey(row)) { parseNamespaceResult(result, visitor); } else if (isTableRowKey(row)) { parseTableResult(result, visitor); } else if (isUserRowKey(row)) { parseUserResult(result, visitor); } else if (isRegionServerRowKey(row)) { parseRegionServerResult(result, visitor); } else if (isExceedThrottleQuotaRowKey(row)) { // skip exceed throttle quota row key if (LOG.isDebugEnabled()) { LOG.debug("Skip exceedThrottleQuota row-key when parse quota result"); } } else { LOG.warn("unexpected row-key: " + Bytes.toString(row)); } } public static void parseResultToCollection(final Result result, Collection quotaSettings) throws IOException { QuotaTableUtil.parseResult(result, new QuotaTableUtil.QuotasVisitor() { @Override public void visitUserQuotas(String userName, Quotas quotas) { quotaSettings.addAll(QuotaSettingsFactory.fromUserQuotas(userName, quotas)); } @Override public void visitUserQuotas(String userName, TableName table, Quotas quotas) { quotaSettings.addAll(QuotaSettingsFactory.fromUserQuotas(userName, table, quotas)); } @Override public void visitUserQuotas(String userName, String namespace, Quotas quotas) { quotaSettings.addAll(QuotaSettingsFactory.fromUserQuotas(userName, namespace, quotas)); } @Override public void visitTableQuotas(TableName tableName, Quotas quotas) { quotaSettings.addAll(QuotaSettingsFactory.fromTableQuotas(tableName, quotas)); } @Override public void visitNamespaceQuotas(String namespace, Quotas quotas) { quotaSettings.addAll(QuotaSettingsFactory.fromNamespaceQuotas(namespace, quotas)); } @Override public void visitRegionServerQuotas(String regionServer, Quotas quotas) { quotaSettings.addAll(QuotaSettingsFactory.fromRegionServerQuotas(regionServer, quotas)); } }); } public static void parseNamespaceResult(final Result result, final NamespaceQuotasVisitor visitor) throws IOException { String namespace = getNamespaceFromRowKey(result.getRow()); parseNamespaceResult(namespace, result, visitor); } protected static void parseNamespaceResult(final String namespace, final Result result, final NamespaceQuotasVisitor visitor) throws IOException { byte[] data = result.getValue(QUOTA_FAMILY_INFO, QUOTA_QUALIFIER_SETTINGS); if (data != null) { Quotas quotas = quotasFromData(data); visitor.visitNamespaceQuotas(namespace, quotas); } } private static void parseRegionServerResult(final Result result, final RegionServerQuotasVisitor visitor) throws IOException { String rs = getRegionServerFromRowKey(result.getRow()); parseRegionServerResult(rs, result, visitor); } private static void parseRegionServerResult(final String regionServer, final Result result, final RegionServerQuotasVisitor visitor) throws IOException { byte[] data = result.getValue(QUOTA_FAMILY_INFO, QUOTA_QUALIFIER_SETTINGS); if (data != null) { Quotas quotas = quotasFromData(data); visitor.visitRegionServerQuotas(regionServer, quotas); } } public static void parseTableResult(final Result result, final TableQuotasVisitor visitor) throws IOException { TableName table = getTableFromRowKey(result.getRow()); parseTableResult(table, result, visitor); } protected static void parseTableResult(final TableName table, final Result result, final TableQuotasVisitor visitor) throws IOException { byte[] data = result.getValue(QUOTA_FAMILY_INFO, QUOTA_QUALIFIER_SETTINGS); if (data != null) { Quotas quotas = quotasFromData(data); visitor.visitTableQuotas(table, quotas); } } public static void parseUserResult(final Result result, final UserQuotasVisitor visitor) throws IOException { String userName = getUserFromRowKey(result.getRow()); parseUserResult(userName, result, visitor); } protected static void parseUserResult(final String userName, final Result result, final UserQuotasVisitor visitor) throws IOException { Map familyMap = result.getFamilyMap(QUOTA_FAMILY_INFO); if (familyMap == null || familyMap.isEmpty()) return; for (Map.Entry entry : familyMap.entrySet()) { Quotas quotas = quotasFromData(entry.getValue()); if (Bytes.startsWith(entry.getKey(), QUOTA_QUALIFIER_SETTINGS_PREFIX)) { String name = Bytes.toString(entry.getKey(), QUOTA_QUALIFIER_SETTINGS_PREFIX.length); if (name.charAt(name.length() - 1) == TableName.NAMESPACE_DELIM) { String namespace = name.substring(0, name.length() - 1); visitor.visitUserQuotas(userName, namespace, quotas); } else { TableName table = TableName.valueOf(name); visitor.visitUserQuotas(userName, table, quotas); } } else if (Bytes.equals(entry.getKey(), QUOTA_QUALIFIER_SETTINGS)) { visitor.visitUserQuotas(userName, quotas); } } } /** * Creates a {@link Put} to store the given {@code snapshot} for the given {@code tableName} in * the quota table. */ static Put createPutForSpaceSnapshot(TableName tableName, SpaceQuotaSnapshot snapshot) { Put p = new Put(getTableRowKey(tableName)); p.addColumn(QUOTA_FAMILY_USAGE, QUOTA_QUALIFIER_POLICY, SpaceQuotaSnapshot.toProtoSnapshot(snapshot).toByteArray()); return p; } /** * Creates a {@link Get} for the HBase snapshot's size against the given table. */ static Get makeGetForSnapshotSize(TableName tn, String snapshot) { Get g = new Get(Bytes.add(QUOTA_TABLE_ROW_KEY_PREFIX, Bytes.toBytes(tn.toString()))); g.addColumn(QUOTA_FAMILY_USAGE, Bytes.add(QUOTA_SNAPSHOT_SIZE_QUALIFIER, Bytes.toBytes(snapshot))); return g; } /** * Creates a {@link Put} to persist the current size of the {@code snapshot} with respect to the * given {@code table}. */ static Put createPutForSnapshotSize(TableName tableName, String snapshot, long size) { // We just need a pb message with some `long usage`, so we can just reuse the // SpaceQuotaSnapshot message instead of creating a new one. Put p = new Put(getTableRowKey(tableName)); p.addColumn(QUOTA_FAMILY_USAGE, getSnapshotSizeQualifier(snapshot), org.apache.hadoop.hbase.shaded.protobuf.generated.QuotaProtos.SpaceQuotaSnapshot.newBuilder() .setQuotaUsage(size).build().toByteArray()); return p; } /** * Creates a {@code Put} for the namespace's total snapshot size. */ static Put createPutForNamespaceSnapshotSize(String namespace, long size) { Put p = new Put(getNamespaceRowKey(namespace)); p.addColumn(QUOTA_FAMILY_USAGE, QUOTA_SNAPSHOT_SIZE_QUALIFIER, org.apache.hadoop.hbase.shaded.protobuf.generated.QuotaProtos.SpaceQuotaSnapshot.newBuilder() .setQuotaUsage(size).build().toByteArray()); return p; } /** * Returns a list of {@code Delete} to remove given table snapshot entries to remove from quota * table * @param snapshotEntriesToRemove the entries to remove */ static List createDeletesForExistingTableSnapshotSizes( Multimap snapshotEntriesToRemove) { List deletes = new ArrayList<>(); for (Map.Entry> entry : snapshotEntriesToRemove.asMap() .entrySet()) { for (String snapshot : entry.getValue()) { Delete d = new Delete(getTableRowKey(entry.getKey())); d.addColumns(QUOTA_FAMILY_USAGE, Bytes.add(QUOTA_SNAPSHOT_SIZE_QUALIFIER, Bytes.toBytes(snapshot))); deletes.add(d); } } return deletes; } /** * Returns a list of {@code Delete} to remove all table snapshot entries from quota table. * @param connection connection to re-use */ static List createDeletesForExistingTableSnapshotSizes(Connection connection) throws IOException { return createDeletesForExistingSnapshotsFromScan(connection, createScanForSpaceSnapshotSizes()); } /** * Returns a list of {@code Delete} to remove given namespace snapshot entries to removefrom quota * table * @param snapshotEntriesToRemove the entries to remove */ static List createDeletesForExistingNamespaceSnapshotSizes(Set snapshotEntriesToRemove) { List deletes = new ArrayList<>(); for (String snapshot : snapshotEntriesToRemove) { Delete d = new Delete(getNamespaceRowKey(snapshot)); d.addColumns(QUOTA_FAMILY_USAGE, QUOTA_SNAPSHOT_SIZE_QUALIFIER); deletes.add(d); } return deletes; } /** * Returns a list of {@code Delete} to remove all namespace snapshot entries from quota table. * @param connection connection to re-use */ static List createDeletesForExistingNamespaceSnapshotSizes(Connection connection) throws IOException { return createDeletesForExistingSnapshotsFromScan(connection, createScanForNamespaceSnapshotSizes()); } /** * Returns a list of {@code Delete} to remove all entries returned by the passed scanner. * @param connection connection to re-use * @param scan the scanner to use to generate the list of deletes */ static List createDeletesForExistingSnapshotsFromScan(Connection connection, Scan scan) throws IOException { List deletes = new ArrayList<>(); try (Table quotaTable = connection.getTable(QUOTA_TABLE_NAME); ResultScanner rs = quotaTable.getScanner(scan)) { for (Result r : rs) { CellScanner cs = r.cellScanner(); while (cs.advance()) { Cell c = cs.current(); byte[] family = Bytes.copy(c.getFamilyArray(), c.getFamilyOffset(), c.getFamilyLength()); byte[] qual = Bytes.copy(c.getQualifierArray(), c.getQualifierOffset(), c.getQualifierLength()); Delete d = new Delete(r.getRow()); d.addColumns(family, qual); deletes.add(d); } } return deletes; } } /** * Remove table usage snapshots (u:p columns) for the namespace passed * @param connection connection to re-use * @param namespace the namespace to fetch the list of table usage snapshots */ static void deleteTableUsageSnapshotsForNamespace(Connection connection, String namespace) throws IOException { Scan s = new Scan(); // Get rows for all tables in namespace s.setStartStopRowForPrefixScan( Bytes.add(QUOTA_TABLE_ROW_KEY_PREFIX, Bytes.toBytes(namespace + TableName.NAMESPACE_DELIM))); // Scan for table usage column (u:p) in quota table s.addColumn(QUOTA_FAMILY_USAGE, QUOTA_QUALIFIER_POLICY); // Scan for table quota column (q:s) if table has a space quota defined s.addColumn(QUOTA_FAMILY_INFO, QUOTA_QUALIFIER_SETTINGS); try (Table quotaTable = connection.getTable(QUOTA_TABLE_NAME); ResultScanner rs = quotaTable.getScanner(s)) { for (Result r : rs) { byte[] data = r.getValue(QUOTA_FAMILY_INFO, QUOTA_QUALIFIER_SETTINGS); // if table does not have a table space quota defined, delete table usage column (u:p) if (data == null) { Delete delete = new Delete(r.getRow()); delete.addColumns(QUOTA_FAMILY_USAGE, QUOTA_QUALIFIER_POLICY); quotaTable.delete(delete); } } } } /** * Fetches the computed size of all snapshots against tables in a namespace for space quotas. */ static long getNamespaceSnapshotSize(Connection conn, String namespace) throws IOException { try (Table quotaTable = conn.getTable(QuotaTableUtil.QUOTA_TABLE_NAME)) { Result r = quotaTable.get(createGetNamespaceSnapshotSize(namespace)); if (r.isEmpty()) { return 0L; } r.advance(); return parseSnapshotSize(r.current()); } catch (InvalidProtocolBufferException e) { throw new IOException("Could not parse snapshot size value for namespace " + namespace, e); } } /** * Creates a {@code Get} to fetch the namespace's total snapshot size. */ static Get createGetNamespaceSnapshotSize(String namespace) { Get g = new Get(getNamespaceRowKey(namespace)); g.addColumn(QUOTA_FAMILY_USAGE, QUOTA_SNAPSHOT_SIZE_QUALIFIER); return g; } /** * Parses the snapshot size from the given Cell's value. */ static long parseSnapshotSize(Cell c) throws InvalidProtocolBufferException { ByteString bs = UnsafeByteOperations.unsafeWrap(c.getValueArray(), c.getValueOffset(), c.getValueLength()); return QuotaProtos.SpaceQuotaSnapshot.parseFrom(bs).getQuotaUsage(); } /** * Returns a scanner for all existing namespace snapshot entries. */ static Scan createScanForNamespaceSnapshotSizes() { return createScanForNamespaceSnapshotSizes(null); } /** * Returns a scanner for all namespace snapshot entries of the given namespace * @param namespace name of the namespace whose snapshot entries are to be scanned */ static Scan createScanForNamespaceSnapshotSizes(String namespace) { Scan s = new Scan(); if (namespace == null || namespace.isEmpty()) { // Read all namespaces, just look at the row prefix s.setStartStopRowForPrefixScan(QUOTA_NAMESPACE_ROW_KEY_PREFIX); } else { // Fetch the exact row for the table byte[] rowkey = getNamespaceRowKey(namespace); // Fetch just this one row s.withStartRow(rowkey).withStopRow(rowkey, true); } // Just the usage family and only the snapshot size qualifiers return s.addFamily(QUOTA_FAMILY_USAGE) .setFilter(new ColumnPrefixFilter(QUOTA_SNAPSHOT_SIZE_QUALIFIER)); } static Scan createScanForSpaceSnapshotSizes() { return createScanForSpaceSnapshotSizes(null); } static Scan createScanForSpaceSnapshotSizes(TableName table) { Scan s = new Scan(); if (null == table) { // Read all tables, just look at the row prefix s.setStartStopRowForPrefixScan(QUOTA_TABLE_ROW_KEY_PREFIX); } else { // Fetch the exact row for the table byte[] rowkey = getTableRowKey(table); // Fetch just this one row s.withStartRow(rowkey).withStopRow(rowkey, true); } // Just the usage family and only the snapshot size qualifiers return s.addFamily(QUOTA_FAMILY_USAGE) .setFilter(new ColumnPrefixFilter(QUOTA_SNAPSHOT_SIZE_QUALIFIER)); } /** * Fetches any persisted HBase snapshot sizes stored in the quota table. The sizes here are * computed relative to the table which the snapshot was created from. A snapshot's size will not * include the size of files which the table still refers. These sizes, in bytes, are what is used * internally to compute quota violation for tables and namespaces. * @return A map of snapshot name to size in bytes per space quota computations */ public static Map getObservedSnapshotSizes(Connection conn) throws IOException { try (Table quotaTable = conn.getTable(QUOTA_TABLE_NAME); ResultScanner rs = quotaTable.getScanner(createScanForSpaceSnapshotSizes())) { final Map snapshotSizes = new HashMap<>(); for (Result r : rs) { CellScanner cs = r.cellScanner(); while (cs.advance()) { Cell c = cs.current(); final String snapshot = extractSnapshotNameFromSizeCell(c); final long size = parseSnapshotSize(c); snapshotSizes.put(snapshot, size); } } return snapshotSizes; } } /** * Returns a multimap for all existing table snapshot entries. * @param conn connection to re-use */ public static Multimap getTableSnapshots(Connection conn) throws IOException { try (Table quotaTable = conn.getTable(QUOTA_TABLE_NAME); ResultScanner rs = quotaTable.getScanner(createScanForSpaceSnapshotSizes())) { Multimap snapshots = HashMultimap.create(); for (Result r : rs) { CellScanner cs = r.cellScanner(); while (cs.advance()) { Cell c = cs.current(); final String snapshot = extractSnapshotNameFromSizeCell(c); snapshots.put(getTableFromRowKey(r.getRow()), snapshot); } } return snapshots; } } /** * Returns a set of the names of all namespaces containing snapshot entries. * @param conn connection to re-use */ public static Set getNamespaceSnapshots(Connection conn) throws IOException { try (Table quotaTable = conn.getTable(QUOTA_TABLE_NAME); ResultScanner rs = quotaTable.getScanner(createScanForNamespaceSnapshotSizes())) { Set snapshots = new HashSet<>(); for (Result r : rs) { CellScanner cs = r.cellScanner(); while (cs.advance()) { cs.current(); snapshots.add(getNamespaceFromRowKey(r.getRow())); } } return snapshots; } } /** * Returns the current space quota snapshot of the given {@code tableName} from * {@code QuotaTableUtil.QUOTA_TABLE_NAME} or null if the no quota information is available for * that tableName. * @param conn connection to re-use * @param tableName name of the table whose current snapshot is to be retreived */ public static SpaceQuotaSnapshot getCurrentSnapshotFromQuotaTable(Connection conn, TableName tableName) throws IOException { try (Table quotaTable = conn.getTable(QuotaTableUtil.QUOTA_TABLE_NAME)) { Map snapshots = new HashMap<>(1); Result result = quotaTable.get(makeQuotaSnapshotGetForTable(tableName)); // if we don't have any row corresponding to this get, return null if (result.isEmpty()) { return null; } // otherwise, extract quota snapshot in snapshots object extractQuotaSnapshot(result, snapshots); return snapshots.get(tableName); } } /* * ========================================================================= Quotas protobuf * helpers */ protected static Quotas quotasFromData(final byte[] data) throws IOException { return quotasFromData(data, 0, data.length); } protected static Quotas quotasFromData(final byte[] data, int offset, int length) throws IOException { int magicLen = ProtobufMagic.lengthOfPBMagic(); if (!ProtobufMagic.isPBMagicPrefix(data, offset, magicLen)) { throw new IOException("Missing pb magic prefix"); } return Quotas.parseFrom(new ByteArrayInputStream(data, offset + magicLen, length - magicLen)); } protected static byte[] quotasToData(final Quotas data) throws IOException { ByteArrayOutputStream stream = new ByteArrayOutputStream(); stream.write(ProtobufMagic.PB_MAGIC); data.writeTo(stream); return stream.toByteArray(); } public static boolean isEmptyQuota(final Quotas quotas) { boolean hasSettings = false; hasSettings |= quotas.hasThrottle(); hasSettings |= quotas.hasBypassGlobals(); // Only when there is a space quota, make sure there's actually both fields provided // Otherwise, it's a noop. if (quotas.hasSpace()) { hasSettings |= (quotas.getSpace().hasSoftLimit() && quotas.getSpace().hasViolationPolicy()); } return !hasSettings; } /* * ========================================================================= HTable helpers */ protected static Result doGet(final Connection connection, final Get get) throws IOException { try (Table table = connection.getTable(QUOTA_TABLE_NAME)) { return table.get(get); } } protected static Result[] doGet(final Connection connection, final List gets) throws IOException { try (Table table = connection.getTable(QUOTA_TABLE_NAME)) { return table.get(gets); } } /* * ========================================================================= Quota table row key * helpers */ protected static byte[] getUserRowKey(final String user) { return Bytes.add(QUOTA_USER_ROW_KEY_PREFIX, Bytes.toBytes(user)); } protected static byte[] getTableRowKey(final TableName table) { return Bytes.add(QUOTA_TABLE_ROW_KEY_PREFIX, table.getName()); } protected static byte[] getNamespaceRowKey(final String namespace) { return Bytes.add(QUOTA_NAMESPACE_ROW_KEY_PREFIX, Bytes.toBytes(namespace)); } protected static byte[] getRegionServerRowKey(final String regionServer) { return Bytes.add(QUOTA_REGION_SERVER_ROW_KEY_PREFIX, Bytes.toBytes(regionServer)); } protected static byte[] getSettingsQualifierForUserTable(final TableName tableName) { return Bytes.add(QUOTA_QUALIFIER_SETTINGS_PREFIX, tableName.getName()); } protected static byte[] getSettingsQualifierForUserNamespace(final String namespace) { return Bytes.add(QUOTA_QUALIFIER_SETTINGS_PREFIX, Bytes.toBytes(namespace + TableName.NAMESPACE_DELIM)); } protected static String getUserRowKeyRegex(final String user) { return getRowKeyRegEx(QUOTA_USER_ROW_KEY_PREFIX, user); } protected static String getTableRowKeyRegex(final String table) { return getRowKeyRegEx(QUOTA_TABLE_ROW_KEY_PREFIX, table); } protected static String getNamespaceRowKeyRegex(final String namespace) { return getRowKeyRegEx(QUOTA_NAMESPACE_ROW_KEY_PREFIX, namespace); } private static String getRegionServerRowKeyRegex(final String regionServer) { return getRowKeyRegEx(QUOTA_REGION_SERVER_ROW_KEY_PREFIX, regionServer); } protected static byte[] getExceedThrottleQuotaRowKey() { return QUOTA_EXCEED_THROTTLE_QUOTA_ROW_KEY; } private static String getRowKeyRegEx(final byte[] prefix, final String regex) { return '^' + Pattern.quote(Bytes.toString(prefix)) + regex + '$'; } protected static String getSettingsQualifierRegexForUserTable(final String table) { return '^' + Pattern.quote(Bytes.toString(QUOTA_QUALIFIER_SETTINGS_PREFIX)) + table + "(?




© 2015 - 2024 Weber Informatics LLC | Privacy Policy