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 */ 018package org.apache.hadoop.hbase.namespace; 019 020import java.io.IOException; 021 022import org.apache.hadoop.hbase.HBaseIOException; 023import org.apache.hadoop.hbase.MetaTableAccessor; 024import org.apache.hadoop.hbase.NamespaceDescriptor; 025import org.apache.hadoop.hbase.TableExistsException; 026import org.apache.hadoop.hbase.TableName; 027import org.apache.hadoop.hbase.client.RegionInfo; 028import org.apache.hadoop.hbase.master.MasterServices; 029import org.apache.hadoop.hbase.quotas.QuotaExceededException; 030import org.apache.yetus.audience.InterfaceAudience; 031import org.slf4j.Logger; 032import org.slf4j.LoggerFactory; 033 034/** 035 * The Class NamespaceAuditor performs checks to ensure operations like table creation 036 * and region splitting preserve namespace quota. The namespace quota can be specified 037 * while namespace creation. 038 */ 039@InterfaceAudience.Private 040public class NamespaceAuditor { 041 private static final Logger LOG = LoggerFactory.getLogger(NamespaceAuditor.class); 042 private NamespaceStateManager stateManager; 043 private MasterServices masterServices; 044 045 public NamespaceAuditor(MasterServices masterServices) { 046 this.masterServices = masterServices; 047 stateManager = new NamespaceStateManager(masterServices); 048 } 049 050 public void start() throws IOException { 051 stateManager.start(); 052 LOG.info("NamespaceAuditor started."); 053 } 054 055 056 /** 057 * Check quota to create table. 058 * We add the table information to namespace state cache, assuming the operation will 059 * pass. If the operation fails, then the next time namespace state chore runs 060 * namespace state cache will be corrected. 061 * 062 * @param tName - The table name to check quota. 063 * @param regions - Number of regions that will be added. 064 * @throws IOException Signals that an I/O exception has occurred. 065 */ 066 public void checkQuotaToCreateTable(TableName tName, int regions) throws IOException { 067 if (stateManager.isInitialized()) { 068 // We do this check to fail fast. 069 if (MetaTableAccessor.tableExists(this.masterServices.getConnection(), tName)) { 070 throw new TableExistsException(tName); 071 } 072 stateManager.checkAndUpdateNamespaceTableCount(tName, regions); 073 } else { 074 checkTableTypeAndThrowException(tName); 075 } 076 } 077 078 /** 079 * Check and update region count quota for an existing table. 080 * @param tName - table name for which region count to be updated. 081 * @param regions - Number of regions that will be added. 082 * @throws IOException Signals that an I/O exception has occurred. 083 */ 084 public void checkQuotaToUpdateRegion(TableName tName, int regions) throws IOException { 085 if (stateManager.isInitialized()) { 086 stateManager.checkAndUpdateNamespaceRegionCount(tName, regions); 087 } else { 088 checkTableTypeAndThrowException(tName); 089 } 090 } 091 092 private void checkTableTypeAndThrowException(TableName name) throws IOException { 093 if (name.isSystemTable()) { 094 LOG.debug("Namespace auditor checks not performed for table " + name.getNameAsString()); 095 } else { 096 throw new HBaseIOException( 097 name + " is being created even before namespace auditor has been initialized."); 098 } 099 } 100 101 /** 102 * Get region count for table 103 * @param tName - table name 104 * @return cached region count, or -1 if table status not found 105 * @throws IOException Signals that the namespace auditor has not been initialized 106 */ 107 public int getRegionCountOfTable(TableName tName) throws IOException { 108 if (stateManager.isInitialized()) { 109 NamespaceTableAndRegionInfo state = stateManager.getState(tName.getNamespaceAsString()); 110 return state != null ? state.getRegionCountOfTable(tName) : -1; 111 } 112 checkTableTypeAndThrowException(tName); 113 return -1; 114 } 115 116 public void checkQuotaToSplitRegion(RegionInfo hri) throws IOException { 117 if (!stateManager.isInitialized()) { 118 throw new IOException( 119 "Split operation is being performed even before namespace auditor is initialized."); 120 } else if (!stateManager 121 .checkAndUpdateNamespaceRegionCount(hri.getTable(), hri.getRegionName(), 1)) { 122 throw new QuotaExceededException("Region split not possible for :" + hri.getEncodedName() 123 + " as quota limits are exceeded "); 124 } 125 } 126 127 public void updateQuotaForRegionMerge(RegionInfo mergedRegion) throws IOException { 128 if (!stateManager.isInitialized()) { 129 throw new IOException( 130 "Merge operation is being performed even before namespace auditor is initialized."); 131 } else if (!stateManager.checkAndUpdateNamespaceRegionCount(mergedRegion.getTable(), 132 mergedRegion.getRegionName(), -1)) { 133 throw new QuotaExceededException("Region merge not possible for :" + 134 mergedRegion.getEncodedName() + " as quota limits are exceeded "); 135 } 136 } 137 138 public void addNamespace(NamespaceDescriptor ns) throws IOException { 139 stateManager.addNamespace(ns.getName()); 140 } 141 142 public void deleteNamespace(String namespace) throws IOException { 143 stateManager.deleteNamespace(namespace); 144 } 145 146 public void removeFromNamespaceUsage(TableName tableName) 147 throws IOException { 148 stateManager.removeTable(tableName); 149 } 150 151 public void removeRegionFromNamespaceUsage(RegionInfo hri) throws IOException { 152 stateManager.removeRegionFromTable(hri); 153 } 154 155 /** 156 * @param namespace The name of the namespace 157 * @return An instance of NamespaceTableAndRegionInfo 158 */ 159 public NamespaceTableAndRegionInfo getState(String namespace) { 160 if (stateManager.isInitialized()) { 161 return stateManager.getState(namespace); 162 } 163 return null; 164 } 165 166 /** 167 * Checks if namespace auditor is initialized. Used only for testing. 168 * 169 * @return true, if is initialized 170 */ 171 public boolean isInitialized() { 172 return stateManager.isInitialized(); 173 } 174}