/** * 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.master; import java.io.IOException; import java.util.List; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.stream.Collectors; import org.apache.commons.lang3.StringUtils; import org.apache.hadoop.hbase.Cell; import org.apache.hadoop.hbase.CellUtil; import org.apache.hadoop.hbase.DoNotRetryIOException; import org.apache.hadoop.hbase.HConstants; import org.apache.hadoop.hbase.MetaTableAccessor; import org.apache.hadoop.hbase.NamespaceDescriptor; import org.apache.hadoop.hbase.TableName; import org.apache.hadoop.hbase.client.BufferedMutator; import org.apache.hadoop.hbase.client.Connection; import org.apache.hadoop.hbase.client.Delete; 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.client.TableDescriptorBuilder; import org.apache.hadoop.hbase.client.TableState; import org.apache.hadoop.hbase.constraint.ConstraintException; import org.apache.hadoop.hbase.master.procedure.DisableTableProcedure; import org.apache.hadoop.hbase.util.Bytes; import org.apache.yetus.audience.InterfaceAudience; import org.apache.hbase.thirdparty.com.google.protobuf.CodedInputStream; import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil; import org.apache.hadoop.hbase.shaded.protobuf.generated.HBaseProtos; /** * This is a helper class used internally to manage the namespace metadata that is stored in the ns * family in meta table. */ @InterfaceAudience.Private public class TableNamespaceManager { public static final String KEY_MAX_REGIONS = "hbase.namespace.quota.maxregions"; public static final String KEY_MAX_TABLES = "hbase.namespace.quota.maxtables"; static final String NS_INIT_TIMEOUT = "hbase.master.namespace.init.timeout"; static final int DEFAULT_NS_INIT_TIMEOUT = 300000; private final ConcurrentMap<String, NamespaceDescriptor> cache = new ConcurrentHashMap<>(); private final MasterServices masterServices; TableNamespaceManager(MasterServices masterServices) { this.masterServices = masterServices; } private void migrateNamespaceTable() throws IOException { try (Table nsTable = masterServices.getConnection().getTable(TableName.NAMESPACE_TABLE_NAME); ResultScanner scanner = nsTable.getScanner( new Scan().addFamily(TableDescriptorBuilder.NAMESPACE_FAMILY_INFO_BYTES).readAllVersions()); BufferedMutator mutator = masterServices.getConnection().getBufferedMutator(TableName.META_TABLE_NAME)) { for (Result result;;) { result = scanner.next(); if (result == null) { break; } Put put = new Put(result.getRow()); result .getColumnCells(TableDescriptorBuilder.NAMESPACE_FAMILY_INFO_BYTES, TableDescriptorBuilder.NAMESPACE_COL_DESC_BYTES) .forEach(c -> put.addColumn(HConstants.NAMESPACE_FAMILY, HConstants.NAMESPACE_COL_DESC_QUALIFIER, c.getTimestamp(), CellUtil.cloneValue(c))); mutator.mutate(put); } } // schedule a disable procedure instead of block waiting here, as when disabling a table we will // wait until master is initialized, but we are part of the initialization... masterServices.getMasterProcedureExecutor().submitProcedure( new DisableTableProcedure(masterServices.getMasterProcedureExecutor().getEnvironment(), TableName.NAMESPACE_TABLE_NAME, false)); } private void loadNamespaceIntoCache() throws IOException { try (Table table = masterServices.getConnection().getTable(TableName.META_TABLE_NAME); ResultScanner scanner = table.getScanner(HConstants.NAMESPACE_FAMILY)) { for (Result result;;) { result = scanner.next(); if (result == null) { break; } Cell cell = result.getColumnLatestCell(HConstants.NAMESPACE_FAMILY, HConstants.NAMESPACE_COL_DESC_QUALIFIER); NamespaceDescriptor ns = ProtobufUtil .toNamespaceDescriptor(HBaseProtos.NamespaceDescriptor.parseFrom(CodedInputStream .newInstance(cell.getValueArray(), cell.getValueOffset(), cell.getValueLength()))); cache.put(ns.getName(), ns); } } } public void start() throws IOException { TableState nsTableState = MetaTableAccessor.getTableState(masterServices.getConnection(), TableName.NAMESPACE_TABLE_NAME); if (nsTableState != null && nsTableState.isEnabled()) { migrateNamespaceTable(); } loadNamespaceIntoCache(); } /** * check whether a namespace has already existed. */ public boolean doesNamespaceExist(String namespaceName) throws IOException { return cache.containsKey(namespaceName); } public NamespaceDescriptor get(String name) throws IOException { return cache.get(name); } public void addOrUpdateNamespace(NamespaceDescriptor ns) throws IOException { insertNamespaceToMeta(masterServices.getConnection(), ns); cache.put(ns.getName(), ns); } public static void insertNamespaceToMeta(Connection conn, NamespaceDescriptor ns) throws IOException { byte[] row = Bytes.toBytes(ns.getName()); Put put = new Put(row, true).addColumn(HConstants.NAMESPACE_FAMILY, HConstants.NAMESPACE_COL_DESC_QUALIFIER, ProtobufUtil.toProtoNamespaceDescriptor(ns).toByteArray()); try (Table table = conn.getTable(TableName.META_TABLE_NAME)) { table.put(put); } } public void deleteNamespace(String namespaceName) throws IOException { Delete d = new Delete(Bytes.toBytes(namespaceName)); try (Table table = masterServices.getConnection().getTable(TableName.META_TABLE_NAME)) { table.delete(d); } cache.remove(namespaceName); } public List<NamespaceDescriptor> list() throws IOException { return cache.values().stream().collect(Collectors.toList()); } public void validateTableAndRegionCount(NamespaceDescriptor desc) throws IOException { if (getMaxRegions(desc) <= 0) { throw new ConstraintException( "The max region quota for " + desc.getName() + " is less than or equal to zero."); } if (getMaxTables(desc) <= 0) { throw new ConstraintException( "The max tables quota for " + desc.getName() + " is less than or equal to zero."); } } public static long getMaxTables(NamespaceDescriptor ns) throws IOException { String value = ns.getConfigurationValue(KEY_MAX_TABLES); long maxTables = 0; if (StringUtils.isNotEmpty(value)) { try { maxTables = Long.parseLong(value); } catch (NumberFormatException exp) { throw new DoNotRetryIOException("NumberFormatException while getting max tables.", exp); } } else { // The property is not set, so assume its the max long value. maxTables = Long.MAX_VALUE; } return maxTables; } public static long getMaxRegions(NamespaceDescriptor ns) throws IOException { String value = ns.getConfigurationValue(KEY_MAX_REGIONS); long maxRegions = 0; if (StringUtils.isNotEmpty(value)) { try { maxRegions = Long.parseLong(value); } catch (NumberFormatException exp) { throw new DoNotRetryIOException("NumberFormatException while getting max regions.", exp); } } else { // The property is not set, so assume its the max long value. maxRegions = Long.MAX_VALUE; } return maxRegions; } }