1
2
3
4
5
6
7
8
9
10
11 package org.apache.hadoop.hbase.namespace;
12
13 import java.io.IOException;
14 import java.util.List;
15 import java.util.Map;
16 import java.util.concurrent.ConcurrentHashMap;
17 import java.util.concurrent.ConcurrentMap;
18
19 import org.apache.commons.logging.Log;
20 import org.apache.commons.logging.LogFactory;
21 import org.apache.hadoop.hbase.HRegionInfo;
22 import org.apache.hadoop.hbase.NamespaceDescriptor;
23 import org.apache.hadoop.hbase.RegionTransition;
24 import org.apache.hadoop.hbase.ServerName;
25 import org.apache.hadoop.hbase.TableName;
26 import org.apache.hadoop.hbase.classification.InterfaceAudience;
27 import org.apache.hadoop.hbase.client.MetaScanner;
28 import org.apache.hadoop.hbase.exceptions.DeserializationException;
29 import org.apache.hadoop.hbase.executor.EventType;
30 import org.apache.hadoop.hbase.master.MasterServices;
31 import org.apache.hadoop.hbase.master.TableNamespaceManager;
32 import org.apache.hadoop.hbase.quotas.QuotaExceededException;
33 import org.apache.hadoop.hbase.util.Bytes;
34 import org.apache.hadoop.hbase.zookeeper.ZKAssign;
35 import org.apache.hadoop.hbase.zookeeper.ZKUtil;
36 import org.apache.hadoop.hbase.zookeeper.ZooKeeperListener;
37 import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher;
38 import org.apache.zookeeper.KeeperException;
39 import org.apache.zookeeper.data.Stat;
40
41
42
43
44
45 @InterfaceAudience.Private
46 class NamespaceStateManager extends ZooKeeperListener {
47
48 private static final Log LOG = LogFactory.getLog(NamespaceStateManager.class);
49 private ConcurrentMap<String, NamespaceTableAndRegionInfo> nsStateCache;
50 private MasterServices master;
51 private volatile boolean initialized = false;
52
53 public NamespaceStateManager(MasterServices masterServices, ZooKeeperWatcher zkw) {
54 super(zkw);
55 nsStateCache = new ConcurrentHashMap<String, NamespaceTableAndRegionInfo>();
56 master = masterServices;
57 }
58
59
60
61
62
63
64 public void start() throws IOException {
65 LOG.info("Namespace State Manager started.");
66 initialize();
67 watcher.registerListenerFirst(this);
68 }
69
70
71
72
73
74
75 public NamespaceTableAndRegionInfo getState(String name) {
76 return nsStateCache.get(name);
77 }
78
79
80
81
82
83
84
85
86
87 synchronized boolean checkAndUpdateNamespaceRegionCount(TableName name, byte[] regionName,
88 int incr) throws IOException {
89 String namespace = name.getNamespaceAsString();
90 NamespaceDescriptor nspdesc = getNamespaceDescriptor(namespace);
91 if (nspdesc != null) {
92 NamespaceTableAndRegionInfo currentStatus;
93 currentStatus = getState(namespace);
94 if (incr > 0
95 && currentStatus.getRegionCount() >= TableNamespaceManager.getMaxRegions(nspdesc)) {
96 LOG.warn("The region " + Bytes.toStringBinary(regionName)
97 + " cannot be created. The region count will exceed quota on the namespace. "
98 + "This may be transient, please retry later if there are any ongoing split"
99 + " operations in the namespace.");
100 return false;
101 }
102 NamespaceTableAndRegionInfo nsInfo = nsStateCache.get(namespace);
103 if (nsInfo != null) {
104 nsInfo.incRegionCountForTable(name, incr);
105 } else {
106 LOG.warn("Namespace state found null for namespace : " + namespace);
107 }
108 }
109 return true;
110 }
111
112
113
114
115
116
117
118
119
120 synchronized void checkAndUpdateNamespaceRegionCount(TableName name, int incr)
121 throws IOException {
122 String namespace = name.getNamespaceAsString();
123 NamespaceDescriptor nspdesc = getNamespaceDescriptor(namespace);
124 if (nspdesc != null) {
125 NamespaceTableAndRegionInfo currentStatus = getState(namespace);
126 int regionCountOfTable = currentStatus.getRegionCountOfTable(name);
127 if ((currentStatus.getRegionCount() - regionCountOfTable + incr) > TableNamespaceManager
128 .getMaxRegions(nspdesc)) {
129 throw new QuotaExceededException("The table " + name.getNameAsString()
130 + " region count cannot be updated as it would exceed maximum number "
131 + "of regions allowed in the namespace. The total number of regions permitted is "
132 + TableNamespaceManager.getMaxRegions(nspdesc));
133 }
134 currentStatus.removeTable(name);
135 currentStatus.addTable(name, incr);
136 }
137 }
138
139 private NamespaceDescriptor getNamespaceDescriptor(String namespaceAsString) {
140 try {
141 return this.master.getNamespaceDescriptor(namespaceAsString);
142 } catch (IOException e) {
143 LOG.error("Error while fetching namespace descriptor for namespace : " + namespaceAsString);
144 return null;
145 }
146 }
147
148 synchronized void checkAndUpdateNamespaceTableCount(TableName table, int numRegions)
149 throws IOException {
150 String namespace = table.getNamespaceAsString();
151 NamespaceDescriptor nspdesc = getNamespaceDescriptor(namespace);
152 if (nspdesc != null) {
153 NamespaceTableAndRegionInfo currentStatus;
154 currentStatus = getState(nspdesc.getName());
155 if ((currentStatus.getTables().size()) >= TableNamespaceManager.getMaxTables(nspdesc)) {
156 throw new QuotaExceededException("The table " + table.getNameAsString()
157 + " cannot be created as it would exceed maximum number of tables allowed "
158 + " in the namespace. The total number of tables permitted is "
159 + TableNamespaceManager.getMaxTables(nspdesc));
160 }
161 if ((currentStatus.getRegionCount() + numRegions) > TableNamespaceManager
162 .getMaxRegions(nspdesc)) {
163 throw new QuotaExceededException("The table " + table.getNameAsString()
164 + " is not allowed to have " + numRegions
165 + " regions. The total number of regions permitted is only "
166 + TableNamespaceManager.getMaxRegions(nspdesc) + ", while current region count is "
167 + currentStatus.getRegionCount()
168 + ". This may be transient, please retry later if there are any"
169 + " ongoing split operations in the namespace.");
170 }
171 } else {
172 throw new IOException("Namespace Descriptor found null for " + namespace
173 + " This is unexpected.");
174 }
175 addTable(table, numRegions);
176 }
177
178 NamespaceTableAndRegionInfo addNamespace(String namespace) {
179 if (!nsStateCache.containsKey(namespace)) {
180 NamespaceTableAndRegionInfo a1 = new NamespaceTableAndRegionInfo(namespace);
181 nsStateCache.put(namespace, a1);
182 }
183 return nsStateCache.get(namespace);
184 }
185
186
187
188
189
190
191 void deleteNamespace(String namespace) {
192 this.nsStateCache.remove(namespace);
193 }
194
195 private void addTable(TableName tableName, int regionCount) throws IOException {
196 NamespaceTableAndRegionInfo info = nsStateCache.get(tableName.getNamespaceAsString());
197 if (info != null) {
198 info.addTable(tableName, regionCount);
199 } else {
200 throw new IOException("Bad state : Namespace quota information not found for namespace : "
201 + tableName.getNamespaceAsString());
202 }
203 }
204
205 synchronized void removeTable(TableName tableName) {
206 NamespaceTableAndRegionInfo info = nsStateCache.get(tableName.getNamespaceAsString());
207 if (info != null) {
208 info.removeTable(tableName);
209 }
210 }
211
212
213
214
215 private void initialize() throws IOException {
216 List<NamespaceDescriptor> namespaces = this.master.listNamespaceDescriptors();
217 for (NamespaceDescriptor namespace : namespaces) {
218 addNamespace(namespace.getName());
219 List<TableName> tables = this.master.listTableNamesByNamespace(namespace.getName());
220 for (TableName table : tables) {
221 if (table.isSystemTable()) {
222 continue;
223 }
224 int regionCount = 0;
225 Map<HRegionInfo, ServerName> regions =
226 MetaScanner.allTableRegions(this.master.getConnection(), table);
227 for (HRegionInfo info : regions.keySet()) {
228 if (!info.isSplit()) {
229 regionCount++;
230 }
231 }
232 addTable(table, regionCount);
233 }
234 }
235 LOG.info("Finished updating state of " + nsStateCache.size() + " namespaces. ");
236 initialized = true;
237 }
238
239 boolean isInitialized() {
240 return initialized;
241 }
242
243 public synchronized void removeRegionFromTable(HRegionInfo hri) throws IOException {
244 String namespace = hri.getTable().getNamespaceAsString();
245 NamespaceTableAndRegionInfo nsInfo = nsStateCache.get(namespace);
246 if (nsInfo != null) {
247 nsInfo.decrementRegionCountForTable(hri.getTable(), 1);
248 } else {
249 throw new IOException("Namespace state found null for namespace : " + namespace);
250 }
251 }
252
253 @Override
254 public void nodeCreated(String path) {
255 checkSplittingOrMergingNode(path);
256 }
257
258 @Override
259 public void nodeChildrenChanged(String path) {
260 checkSplittingOrMergingNode(path);
261 }
262
263 private void checkSplittingOrMergingNode(String path) {
264 String msg = "Error reading data from zookeeper, ";
265 try {
266 if (path.startsWith(watcher.assignmentZNode)) {
267 List<String> children =
268 ZKUtil.listChildrenAndWatchForNewChildren(watcher, watcher.assignmentZNode);
269 if (children != null) {
270 for (String child : children) {
271 Stat stat = new Stat();
272 byte[] data =
273 ZKAssign.getDataAndWatch(watcher, ZKUtil.joinZNode(watcher.assignmentZNode, child),
274 stat);
275 if (data != null) {
276 RegionTransition rt = RegionTransition.parseFrom(data);
277 if (rt.getEventType().equals(EventType.RS_ZK_REQUEST_REGION_SPLIT)) {
278 TableName table = HRegionInfo.getTable(rt.getRegionName());
279 if (!checkAndUpdateNamespaceRegionCount(table, rt.getRegionName(), 1)) {
280 ZKUtil.deleteNode(watcher, ZKUtil.joinZNode(watcher.assignmentZNode, child));
281 }
282 } else if (rt.getEventType().equals(EventType.RS_ZK_REQUEST_REGION_MERGE)) {
283 TableName table = HRegionInfo.getTable(rt.getRegionName());
284 checkAndUpdateNamespaceRegionCount(table, rt.getRegionName(), -1);
285 }
286 }
287 }
288 }
289 }
290 } catch (KeeperException ke) {
291 LOG.error(msg, ke);
292 watcher.abort(msg, ke);
293 } catch (DeserializationException e) {
294 LOG.error(msg, e);
295 watcher.abort(msg, e);
296 } catch (IOException e) {
297 LOG.error(msg, e);
298 watcher.abort(msg, e);
299 }
300 }
301 }