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 void deleteNamespace(String namespace) {
191 this.nsStateCache.remove(namespace);
192 }
193
194 private void addTable(TableName tableName, int regionCount) throws IOException {
195 NamespaceTableAndRegionInfo info = nsStateCache.get(tableName.getNamespaceAsString());
196 if (info != null) {
197 info.addTable(tableName, regionCount);
198 } else {
199 throw new IOException("Bad state : Namespace quota information not found for namespace : "
200 + tableName.getNamespaceAsString());
201 }
202 }
203
204 synchronized void removeTable(TableName tableName) {
205 NamespaceTableAndRegionInfo info = nsStateCache.get(tableName.getNamespaceAsString());
206 if (info != null) {
207 info.removeTable(tableName);
208 }
209 }
210
211
212
213
214 private void initialize() throws IOException {
215 List<NamespaceDescriptor> namespaces = this.master.listNamespaceDescriptors();
216 for (NamespaceDescriptor namespace : namespaces) {
217 addNamespace(namespace.getName());
218 List<TableName> tables = this.master.listTableNamesByNamespace(namespace.getName());
219 for (TableName table : tables) {
220 if (table.isSystemTable()) {
221 continue;
222 }
223 int regionCount = 0;
224 Map<HRegionInfo, ServerName> regions =
225 MetaScanner.allTableRegions(this.master.getConnection(), table);
226 for (HRegionInfo info : regions.keySet()) {
227 if (!info.isSplit()) {
228 regionCount++;
229 }
230 }
231 addTable(table, regionCount);
232 }
233 }
234 LOG.info("Finished updating state of " + nsStateCache.size() + " namespaces. ");
235 initialized = true;
236 }
237
238 boolean isInitialized() {
239 return initialized;
240 }
241
242 public synchronized void removeRegionFromTable(HRegionInfo hri) throws IOException {
243 String namespace = hri.getTable().getNamespaceAsString();
244 NamespaceTableAndRegionInfo nsInfo = nsStateCache.get(namespace);
245 if (nsInfo != null) {
246 nsInfo.decrementRegionCountForTable(hri.getTable(), 1);
247 } else {
248 throw new IOException("Namespace state found null for namespace : " + namespace);
249 }
250 }
251
252 @Override
253 public void nodeCreated(String path) {
254 checkSplittingOrMergingNode(path);
255 }
256
257 @Override
258 public void nodeChildrenChanged(String path) {
259 checkSplittingOrMergingNode(path);
260 }
261
262 private void checkSplittingOrMergingNode(String path) {
263 String msg = "Error reading data from zookeeper, ";
264 try {
265 if (path.startsWith(watcher.assignmentZNode)) {
266 List<String> children =
267 ZKUtil.listChildrenAndWatchForNewChildren(watcher, watcher.assignmentZNode);
268 if (children != null) {
269 for (String child : children) {
270 Stat stat = new Stat();
271 byte[] data =
272 ZKAssign.getDataAndWatch(watcher, ZKUtil.joinZNode(watcher.assignmentZNode, child),
273 stat);
274 if (data != null) {
275 RegionTransition rt = RegionTransition.parseFrom(data);
276 if (rt.getEventType().equals(EventType.RS_ZK_REQUEST_REGION_SPLIT)) {
277 TableName table = HRegionInfo.getTable(rt.getRegionName());
278 if (!checkAndUpdateNamespaceRegionCount(table, rt.getRegionName(), 1)) {
279 ZKUtil.deleteNode(watcher, ZKUtil.joinZNode(watcher.assignmentZNode, child));
280 }
281 } else if (rt.getEventType().equals(EventType.RS_ZK_REQUEST_REGION_MERGE)) {
282 TableName table = HRegionInfo.getTable(rt.getRegionName());
283 checkAndUpdateNamespaceRegionCount(table, rt.getRegionName(), -1);
284 }
285 }
286 }
287 }
288 }
289 } catch (KeeperException ke) {
290 LOG.error(msg, ke);
291 watcher.abort(msg, ke);
292 } catch (DeserializationException e) {
293 LOG.error(msg, e);
294 watcher.abort(msg, e);
295 } catch (IOException e) {
296 LOG.error(msg, e);
297 watcher.abort(msg, e);
298 }
299 }
300 }