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}