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  /**
28   * The Class NamespaceAuditor performs checks to ensure operations like table creation and region
29   * splitting preserve namespace quota. The namespace quota can be specified while namespace
30   * creation.
31   */
32  @InterfaceAudience.Private
33  public class NamespaceAuditor {
34    private static final Log LOG = LogFactory.getLog(NamespaceAuditor.class);
35    static final String NS_AUDITOR_INIT_TIMEOUT = "hbase.namespace.auditor.init.timeout";
36    static final int DEFAULT_NS_AUDITOR_INIT_TIMEOUT = 120000;
37    private NamespaceStateManager stateManager;
38    private MasterServices masterServices;
39  
40    public NamespaceAuditor(MasterServices masterServices) {
41      this.masterServices = masterServices;
42      stateManager = new NamespaceStateManager(masterServices, masterServices.getZooKeeper());
43    }
44  
45    public void start() throws IOException {
46      stateManager.start();
47      LOG.info("NamespaceAuditor started.");
48    }
49  
50    /**
51     * Check quota to create table. We add the table information to namespace state cache, assuming
52     * the operation will pass. If the operation fails, then the next time namespace state chore runs
53     * namespace state cache will be corrected.
54     * @param tName - The table name to check quota.
55     * @param regions - Number of regions that will be added.
56     * @throws IOException Signals that an I/O exception has occurred.
57     */
58    public void checkQuotaToCreateTable(TableName tName, int regions) throws IOException {
59      if (stateManager.isInitialized()) {
60        // We do this check to fail fast.
61        if (MetaTableAccessor.tableExists(this.masterServices.getConnection(), tName)) {
62          throw new TableExistsException(tName);
63        }
64        stateManager.checkAndUpdateNamespaceTableCount(tName, regions);
65      } else {
66        checkTableTypeAndThrowException(tName);
67      }
68    }
69    
70    /**
71     * Check and update region count quota for an existing table.
72     * @param tName - table name for which region count to be updated.
73     * @param regions - Number of regions that will be added.
74     * @throws IOException Signals that an I/O exception has occurred.
75     */
76    public void checkQuotaToUpdateRegion(TableName tName, int regions) throws IOException {
77      if (stateManager.isInitialized()) {
78        stateManager.checkAndUpdateNamespaceRegionCount(tName, regions);
79      } else {
80        checkTableTypeAndThrowException(tName);
81      }
82    }
83  
84    private void checkTableTypeAndThrowException(TableName name) throws IOException {
85      if (name.isSystemTable()) {
86        LOG.debug("Namespace auditor checks not performed for table " + name.getNameAsString());
87      } else {
88        throw new HBaseIOException(name
89            + " is being created even before namespace auditor has been initialized.");
90      }
91    }
92  
93    /**
94     * Get region count for table
95     * @param tName - table name
96     * @return cached region count, or -1 if table status not found
97     * @throws IOException Signals that the namespace auditor has not been initialized
98     */
99    public int getRegionCountOfTable(TableName tName) throws IOException {
100     if (stateManager.isInitialized()) {
101       NamespaceTableAndRegionInfo state = stateManager.getState(tName.getNamespaceAsString());
102       return state != null ? state.getRegionCountOfTable(tName) : -1;
103     }
104     checkTableTypeAndThrowException(tName);
105     return -1;
106   }
107 
108   public void checkQuotaToSplitRegion(HRegionInfo hri) throws IOException {
109     if (!stateManager.isInitialized()) {
110       throw new IOException(
111           "Split operation is being performed even before namespace auditor is initialized.");
112     } else if (!stateManager.checkAndUpdateNamespaceRegionCount(hri.getTable(),
113       hri.getRegionName(), 1)) {
114       throw new QuotaExceededException("Region split not possible for :" + hri.getEncodedName()
115           + " as quota limits are exceeded ");
116     }
117   }
118 
119   public void updateQuotaForRegionMerge(HRegionInfo hri) throws IOException {
120     if (!stateManager.isInitialized()) {
121       throw new IOException(
122           "Merge operation is being performed even before namespace auditor is initialized.");
123     } else if (!stateManager
124         .checkAndUpdateNamespaceRegionCount(hri.getTable(), hri.getRegionName(), -1)) {
125       throw new QuotaExceededException("Region split not possible for :" + hri.getEncodedName()
126           + " as quota limits are exceeded ");
127     }
128   }
129 
130   public void addNamespace(NamespaceDescriptor ns) throws IOException {
131     stateManager.addNamespace(ns.getName());
132   }
133 
134   public void deleteNamespace(String namespace) throws IOException {
135     stateManager.deleteNamespace(namespace);
136   }
137 
138   public void removeFromNamespaceUsage(TableName tableName) throws IOException {
139     stateManager.removeTable(tableName);
140   }
141 
142   public void removeRegionFromNamespaceUsage(HRegionInfo hri) throws IOException {
143     stateManager.removeRegionFromTable(hri);
144   }
145 
146   /**
147    * @param namespace The name of the namespace
148    * @return An instance of NamespaceTableAndRegionInfo
149    */
150   public NamespaceTableAndRegionInfo getState(String namespace) {
151     if (stateManager.isInitialized()) {
152       return stateManager.getState(namespace);
153     }
154     return null;
155   }
156 
157   /**
158    * Checks if namespace auditor is initialized. Used only for testing.
159    * @return true, if is initialized
160    */
161   public boolean isInitialized() {
162     return stateManager.isInitialized();
163   }
164 }