View Javadoc

1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
3    * agreements. See the NOTICE file distributed with this work for additional information regarding
4    * copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the
5    * "License"); you may not use this file except in compliance with the License. You may obtain a
6    * copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable
7    * law or agreed to in writing, software distributed under the License is distributed on an "AS IS"
8    * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
9    * for the specific language governing permissions and limitations under the License.
10   */
11  package org.apache.hadoop.hbase.namespace;
12  
13  import java.io.IOException;
14  
15  import org.apache.commons.logging.Log;
16  import org.apache.commons.logging.LogFactory;
17  import org.apache.hadoop.hbase.HBaseIOException;
18  import org.apache.hadoop.hbase.HRegionInfo;
19  import org.apache.hadoop.hbase.MetaTableAccessor;
20  import org.apache.hadoop.hbase.NamespaceDescriptor;
21  import org.apache.hadoop.hbase.TableExistsException;
22  import org.apache.hadoop.hbase.TableName;
23  import org.apache.hadoop.hbase.classification.InterfaceAudience;
24  import org.apache.hadoop.hbase.master.MasterServices;
25  import org.apache.hadoop.hbase.quotas.QuotaExceededException;
26  
27  import com.google.common.annotations.VisibleForTesting;
28  
29  /**
30   * The Class NamespaceAuditor performs checks to ensure operations like table creation and region
31   * splitting preserve namespace quota. The namespace quota can be specified while namespace
32   * creation.
33   */
34  @InterfaceAudience.Private
35  public class NamespaceAuditor {
36    private static Log LOG = LogFactory.getLog(NamespaceAuditor.class);
37    static final String NS_AUDITOR_INIT_TIMEOUT = "hbase.namespace.auditor.init.timeout";
38    static final int DEFAULT_NS_AUDITOR_INIT_TIMEOUT = 120000;
39    private NamespaceStateManager stateManager;
40    private MasterServices masterServices;
41  
42    public NamespaceAuditor(MasterServices masterServices) {
43      this.masterServices = masterServices;
44      stateManager = new NamespaceStateManager(masterServices, masterServices.getZooKeeper());
45    }
46  
47    public void start() throws IOException {
48      stateManager.start();
49      LOG.info("NamespaceAuditor started.");
50    }
51  
52    /**
53     * Check quota to create table. We add the table information to namespace state cache, assuming
54     * the operation will pass. If the operation fails, then the next time namespace state chore runs
55     * namespace state cache will be corrected.
56     * @param tName - The table name to check quota.
57     * @param regions - Number of regions that will be added.
58     * @throws IOException Signals that an I/O exception has occurred.
59     */
60    public void checkQuotaToCreateTable(TableName tName, int regions) throws IOException {
61      if (stateManager.isInitialized()) {
62        // We do this check to fail fast.
63        if (MetaTableAccessor.tableExists(this.masterServices.getConnection(), tName)) {
64          throw new TableExistsException(tName);
65        }
66        stateManager.checkAndUpdateNamespaceTableCount(tName, regions);
67      } else {
68        checkTableTypeAndThrowException(tName);
69      }
70    }
71    
72    /**
73     * Check and update region count quota for an existing table.
74     * @param tName - table name for which region count to be updated.
75     * @param regions - Number of regions that will be added.
76     * @throws IOException Signals that an I/O exception has occurred.
77     */
78    public void checkQuotaToUpdateRegion(TableName tName, int regions) throws IOException {
79      if (stateManager.isInitialized()) {
80        stateManager.checkAndUpdateNamespaceRegionCount(tName, regions);
81      } else {
82        checkTableTypeAndThrowException(tName);
83      }
84    }
85  
86    private void checkTableTypeAndThrowException(TableName name) throws IOException {
87      if (name.isSystemTable()) {
88        LOG.debug("Namespace auditor checks not performed for table " + name.getNameAsString());
89      } else {
90        throw new HBaseIOException(name
91            + " is being created even before namespace auditor has been initialized.");
92      }
93    }
94  
95    /**
96     * Get region count for table
97     * @param tName - table name
98     * @return cached region count, or -1 if table status not found
99     * @throws IOException Signals that the namespace auditor has not been initialized
100    */
101   public int getRegionCountOfTable(TableName tName) throws IOException {
102     if (stateManager.isInitialized()) {
103       NamespaceTableAndRegionInfo state = stateManager.getState(tName.getNamespaceAsString());
104       return state != null ? state.getRegionCountOfTable(tName) : -1;
105     }
106     checkTableTypeAndThrowException(tName);
107     return -1;
108   }
109 
110   public void checkQuotaToSplitRegion(HRegionInfo hri) throws IOException {
111     if (!stateManager.isInitialized()) {
112       throw new IOException(
113           "Split operation is being performed even before namespace auditor is initialized.");
114     } else if (!stateManager.checkAndUpdateNamespaceRegionCount(hri.getTable(),
115       hri.getRegionName(), 1)) {
116       throw new QuotaExceededException("Region split not possible for :" + hri.getEncodedName()
117           + " as quota limits are exceeded ");
118     }
119   }
120 
121   public void updateQuotaForRegionMerge(HRegionInfo hri) throws IOException {
122     if (!stateManager.isInitialized()) {
123       throw new IOException(
124           "Merge operation is being performed even before namespace auditor is initialized.");
125     } else if (!stateManager
126         .checkAndUpdateNamespaceRegionCount(hri.getTable(), hri.getRegionName(), -1)) {
127       throw new QuotaExceededException("Region split not possible for :" + hri.getEncodedName()
128           + " as quota limits are exceeded ");
129     }
130   }
131 
132   public void addNamespace(NamespaceDescriptor ns) throws IOException {
133     stateManager.addNamespace(ns.getName());
134   }
135 
136   public void deleteNamespace(String namespace) throws IOException {
137     stateManager.deleteNamespace(namespace);
138   }
139 
140   public void removeFromNamespaceUsage(TableName tableName) throws IOException {
141     stateManager.removeTable(tableName);
142   }
143 
144   public void removeRegionFromNamespaceUsage(HRegionInfo hri) throws IOException {
145     stateManager.removeRegionFromTable(hri);
146   }
147 
148   /**
149    * Used only for unit tests.
150    * @param namespace The name of the namespace
151    * @return An instance of NamespaceTableAndRegionInfo
152    */
153   @VisibleForTesting
154   NamespaceTableAndRegionInfo getState(String namespace) {
155     if (stateManager.isInitialized()) {
156       return stateManager.getState(namespace);
157     }
158     return null;
159   }
160 
161   /**
162    * Checks if namespace auditor is initialized. Used only for testing.
163    * @return true, if is initialized
164    */
165   public boolean isInitialized() {
166     return stateManager.isInitialized();
167   }
168 }