1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.hadoop.hbase.master;
20
21 import java.io.FileNotFoundException;
22 import java.io.IOException;
23 import java.io.InterruptedIOException;
24 import java.util.NavigableSet;
25
26 import org.apache.commons.lang.StringUtils;
27 import org.apache.commons.logging.Log;
28 import org.apache.commons.logging.LogFactory;
29 import org.apache.hadoop.hbase.classification.InterfaceAudience;
30 import org.apache.hadoop.conf.Configuration;
31 import org.apache.hadoop.fs.FileStatus;
32 import org.apache.hadoop.fs.FileSystem;
33 import org.apache.hadoop.hbase.CellUtil;
34 import org.apache.hadoop.hbase.DoNotRetryIOException;
35 import org.apache.hadoop.hbase.HConstants;
36 import org.apache.hadoop.hbase.HRegionInfo;
37 import org.apache.hadoop.hbase.HTableDescriptor;
38 import org.apache.hadoop.hbase.NamespaceDescriptor;
39 import org.apache.hadoop.hbase.NamespaceExistException;
40 import org.apache.hadoop.hbase.NamespaceNotFoundException;
41 import org.apache.hadoop.hbase.TableName;
42 import org.apache.hadoop.hbase.ZKNamespaceManager;
43 import org.apache.hadoop.hbase.MetaTableAccessor;
44 import org.apache.hadoop.hbase.client.Delete;
45 import org.apache.hadoop.hbase.client.Get;
46 import org.apache.hadoop.hbase.client.Put;
47 import org.apache.hadoop.hbase.client.Result;
48 import org.apache.hadoop.hbase.client.ResultScanner;
49 import org.apache.hadoop.hbase.client.Table;
50 import org.apache.hadoop.hbase.constraint.ConstraintException;
51 import org.apache.hadoop.hbase.master.procedure.CreateTableProcedure;
52 import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
53 import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos;
54 import org.apache.hadoop.hbase.util.Bytes;
55 import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
56 import org.apache.hadoop.hbase.util.FSUtils;
57
58 import com.google.common.collect.Sets;
59
60
61
62
63
64
65
66 @InterfaceAudience.Private
67 @edu.umd.cs.findbugs.annotations.SuppressWarnings(value="IS2_INCONSISTENT_SYNC",
68 justification="TODO: synchronize access on nsTable but it is done in tiers above and this " +
69 "class is going away/shrinking")
70 public class TableNamespaceManager {
71 private static final Log LOG = LogFactory.getLog(TableNamespaceManager.class);
72
73 private Configuration conf;
74 private MasterServices masterServices;
75 private Table nsTable = null;
76 private ZKNamespaceManager zkNamespaceManager;
77 private boolean initialized;
78
79 public static final String KEY_MAX_REGIONS = "hbase.namespace.quota.maxregions";
80 public static final String KEY_MAX_TABLES = "hbase.namespace.quota.maxtables";
81
82 static final String NS_INIT_TIMEOUT = "hbase.master.namespace.init.timeout";
83 static final int DEFAULT_NS_INIT_TIMEOUT = 300000;
84
85 public TableNamespaceManager(MasterServices masterServices) {
86 this.masterServices = masterServices;
87 this.conf = masterServices.getConfiguration();
88 }
89
90 public void start() throws IOException {
91 if (!MetaTableAccessor.tableExists(masterServices.getConnection(),
92 TableName.NAMESPACE_TABLE_NAME)) {
93 LOG.info("Namespace table not found. Creating...");
94 createNamespaceTable(masterServices);
95 }
96
97 try {
98
99
100
101 long startTime = EnvironmentEdgeManager.currentTime();
102 int timeout = conf.getInt(NS_INIT_TIMEOUT, DEFAULT_NS_INIT_TIMEOUT);
103 while (!isTableAssigned()) {
104 if (EnvironmentEdgeManager.currentTime() - startTime + 100 > timeout) {
105
106 throw new IOException("Timedout " + timeout + "ms waiting for namespace table to " +
107 "be assigned");
108 }
109 Thread.sleep(100);
110 }
111 } catch (InterruptedException e) {
112 throw (InterruptedIOException)new InterruptedIOException().initCause(e);
113 }
114
115
116 isTableAvailableAndInitialized();
117 }
118
119 private synchronized Table getNamespaceTable() throws IOException {
120 if (!isTableAvailableAndInitialized()) {
121 throw new IOException(this.getClass().getName() + " isn't ready to serve");
122 }
123 return nsTable;
124 }
125
126
127 public synchronized NamespaceDescriptor get(String name) throws IOException {
128 if (!isTableAvailableAndInitialized()) return null;
129 return zkNamespaceManager.get(name);
130 }
131
132 public synchronized void create(NamespaceDescriptor ns) throws IOException {
133 create(getNamespaceTable(), ns);
134 }
135
136 public synchronized void update(NamespaceDescriptor ns) throws IOException {
137 Table table = getNamespaceTable();
138 if (get(table, ns.getName()) == null) {
139 throw new NamespaceNotFoundException(ns.getName());
140 }
141 upsert(table, ns);
142 }
143
144 private NamespaceDescriptor get(Table table, String name) throws IOException {
145 Result res = table.get(new Get(Bytes.toBytes(name)));
146 if (res.isEmpty()) {
147 return null;
148 }
149 byte[] val = CellUtil.cloneValue(res.getColumnLatestCell(
150 HTableDescriptor.NAMESPACE_FAMILY_INFO_BYTES, HTableDescriptor.NAMESPACE_COL_DESC_BYTES));
151 return
152 ProtobufUtil.toNamespaceDescriptor(
153 HBaseProtos.NamespaceDescriptor.parseFrom(val));
154 }
155
156 private void create(Table table, NamespaceDescriptor ns) throws IOException {
157 if (get(table, ns.getName()) != null) {
158 throw new NamespaceExistException(ns.getName());
159 }
160 validateTableAndRegionCount(ns);
161 FileSystem fs = masterServices.getMasterFileSystem().getFileSystem();
162 fs.mkdirs(FSUtils.getNamespaceDir(
163 masterServices.getMasterFileSystem().getRootDir(), ns.getName()));
164 upsert(table, ns);
165 if (this.masterServices.isInitialized()) {
166 this.masterServices.getMasterQuotaManager().setNamespaceQuota(ns);
167 }
168 }
169
170 private void upsert(Table table, NamespaceDescriptor ns) throws IOException {
171 validateTableAndRegionCount(ns);
172 Put p = new Put(Bytes.toBytes(ns.getName()));
173 p.addImmutable(HTableDescriptor.NAMESPACE_FAMILY_INFO_BYTES,
174 HTableDescriptor.NAMESPACE_COL_DESC_BYTES,
175 ProtobufUtil.toProtoNamespaceDescriptor(ns).toByteArray());
176 table.put(p);
177 try {
178 zkNamespaceManager.update(ns);
179 } catch(IOException ex) {
180 String msg = "Failed to update namespace information in ZK. Aborting.";
181 LOG.fatal(msg, ex);
182 masterServices.abort(msg, ex);
183 }
184 }
185
186 public synchronized void remove(String name) throws IOException {
187 if (get(name) == null) {
188 throw new NamespaceNotFoundException(name);
189 }
190 if (NamespaceDescriptor.RESERVED_NAMESPACES.contains(name)) {
191 throw new ConstraintException("Reserved namespace "+name+" cannot be removed.");
192 }
193 int tableCount;
194 try {
195 tableCount = masterServices.listTableDescriptorsByNamespace(name).size();
196 } catch (FileNotFoundException fnfe) {
197 throw new NamespaceNotFoundException(name);
198 }
199 if (tableCount > 0) {
200 throw new ConstraintException("Only empty namespaces can be removed. " +
201 "Namespace "+name+" has "+tableCount+" tables");
202 }
203 Delete d = new Delete(Bytes.toBytes(name));
204 getNamespaceTable().delete(d);
205
206
207 zkNamespaceManager.remove(name);
208 FileSystem fs = masterServices.getMasterFileSystem().getFileSystem();
209 for(FileStatus status :
210 fs.listStatus(FSUtils.getNamespaceDir(
211 masterServices.getMasterFileSystem().getRootDir(), name))) {
212 if (!HConstants.HBASE_NON_TABLE_DIRS.contains(status.getPath().getName())) {
213 throw new IOException("Namespace directory contains table dir: "+status.getPath());
214 }
215 }
216 if (!fs.delete(FSUtils.getNamespaceDir(
217 masterServices.getMasterFileSystem().getRootDir(), name), true)) {
218 throw new IOException("Failed to remove namespace: "+name);
219 }
220 this.masterServices.getMasterQuotaManager().removeNamespaceQuota(name);
221 }
222
223 public synchronized NavigableSet<NamespaceDescriptor> list() throws IOException {
224 NavigableSet<NamespaceDescriptor> ret =
225 Sets.newTreeSet(NamespaceDescriptor.NAMESPACE_DESCRIPTOR_COMPARATOR);
226 ResultScanner scanner = getNamespaceTable().getScanner(HTableDescriptor.NAMESPACE_FAMILY_INFO_BYTES);
227 try {
228 for(Result r : scanner) {
229 byte[] val = CellUtil.cloneValue(r.getColumnLatestCell(
230 HTableDescriptor.NAMESPACE_FAMILY_INFO_BYTES,
231 HTableDescriptor.NAMESPACE_COL_DESC_BYTES));
232 ret.add(ProtobufUtil.toNamespaceDescriptor(
233 HBaseProtos.NamespaceDescriptor.parseFrom(val)));
234 }
235 } finally {
236 scanner.close();
237 }
238 return ret;
239 }
240
241 private void createNamespaceTable(MasterServices masterServices) throws IOException {
242 HRegionInfo[] newRegions = new HRegionInfo[]{
243 new HRegionInfo(HTableDescriptor.NAMESPACE_TABLEDESC.getTableName(), null, null)};
244
245
246 masterServices.getMasterProcedureExecutor()
247 .submitProcedure(new CreateTableProcedure(
248 masterServices.getMasterProcedureExecutor().getEnvironment(),
249 HTableDescriptor.NAMESPACE_TABLEDESC,
250 newRegions));
251 }
252
253
254
255
256
257
258
259
260
261 @SuppressWarnings("deprecation")
262 public synchronized boolean isTableAvailableAndInitialized() throws IOException {
263
264 if (initialized) {
265 this.nsTable = this.masterServices.getConnection().getTable(TableName.NAMESPACE_TABLE_NAME);
266 return true;
267 }
268
269
270 if (isTableAssigned()) {
271 try {
272 nsTable = this.masterServices.getConnection().getTable(TableName.NAMESPACE_TABLE_NAME);
273 zkNamespaceManager = new ZKNamespaceManager(masterServices.getZooKeeper());
274 zkNamespaceManager.start();
275
276 if (get(nsTable, NamespaceDescriptor.DEFAULT_NAMESPACE.getName()) == null) {
277 create(nsTable, NamespaceDescriptor.DEFAULT_NAMESPACE);
278 }
279 if (get(nsTable, NamespaceDescriptor.SYSTEM_NAMESPACE.getName()) == null) {
280 create(nsTable, NamespaceDescriptor.SYSTEM_NAMESPACE);
281 }
282
283 ResultScanner scanner = nsTable.getScanner(HTableDescriptor.NAMESPACE_FAMILY_INFO_BYTES);
284 try {
285 for (Result result : scanner) {
286 byte[] val = CellUtil.cloneValue(result.getColumnLatest(
287 HTableDescriptor.NAMESPACE_FAMILY_INFO_BYTES,
288 HTableDescriptor.NAMESPACE_COL_DESC_BYTES));
289 NamespaceDescriptor ns =
290 ProtobufUtil.toNamespaceDescriptor(
291 HBaseProtos.NamespaceDescriptor.parseFrom(val));
292 zkNamespaceManager.update(ns);
293 }
294 } finally {
295 scanner.close();
296 }
297 initialized = true;
298 return true;
299 } catch (IOException ie) {
300 LOG.warn("Caught exception in initializing namespace table manager", ie);
301 if (nsTable != null) {
302 nsTable.close();
303 }
304 throw ie;
305 }
306 }
307 return false;
308 }
309
310 private boolean isTableAssigned() {
311 return !masterServices.getAssignmentManager().getRegionStates().
312 getRegionsOfTable(TableName.NAMESPACE_TABLE_NAME).isEmpty();
313 }
314
315 void validateTableAndRegionCount(NamespaceDescriptor desc) throws IOException {
316 if (getMaxRegions(desc) <= 0) {
317 throw new ConstraintException("The max region quota for " + desc.getName()
318 + " is less than or equal to zero.");
319 }
320 if (getMaxTables(desc) <= 0) {
321 throw new ConstraintException("The max tables quota for " + desc.getName()
322 + " is less than or equal to zero.");
323 }
324 }
325
326 public static long getMaxTables(NamespaceDescriptor ns) throws IOException {
327 String value = ns.getConfigurationValue(KEY_MAX_TABLES);
328 long maxTables = 0;
329 if (StringUtils.isNotEmpty(value)) {
330 try {
331 maxTables = Long.parseLong(value);
332 } catch (NumberFormatException exp) {
333 throw new DoNotRetryIOException("NumberFormatException while getting max tables.", exp);
334 }
335 } else {
336
337 maxTables = Long.MAX_VALUE;
338 }
339 return maxTables;
340 }
341
342 public static long getMaxRegions(NamespaceDescriptor ns) throws IOException {
343 String value = ns.getConfigurationValue(KEY_MAX_REGIONS);
344 long maxRegions = 0;
345 if (StringUtils.isNotEmpty(value)) {
346 try {
347 maxRegions = Long.parseLong(value);
348 } catch (NumberFormatException exp) {
349 throw new DoNotRetryIOException("NumberFormatException while getting max regions.", exp);
350 }
351 } else {
352
353 maxRegions = Long.MAX_VALUE;
354 }
355 return maxRegions;
356 }
357 }