001/** 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018 019package org.apache.hadoop.hbase.master; 020 021import java.io.IOException; 022import java.io.InterruptedIOException; 023import java.util.NavigableSet; 024 025import org.apache.commons.lang3.StringUtils; 026import org.apache.hadoop.conf.Configuration; 027import org.apache.hadoop.hbase.Cell; 028import org.apache.hadoop.hbase.CellBuilderFactory; 029import org.apache.hadoop.hbase.CellBuilderType; 030import org.apache.hadoop.hbase.CellUtil; 031import org.apache.hadoop.hbase.DoNotRetryIOException; 032import org.apache.hadoop.hbase.HTableDescriptor; 033import org.apache.hadoop.hbase.MetaTableAccessor; 034import org.apache.hadoop.hbase.NamespaceDescriptor; 035import org.apache.hadoop.hbase.Stoppable; 036import org.apache.hadoop.hbase.TableName; 037import org.apache.hadoop.hbase.ZKNamespaceManager; 038import org.apache.hadoop.hbase.client.Delete; 039import org.apache.hadoop.hbase.client.Get; 040import org.apache.hadoop.hbase.client.Put; 041import org.apache.hadoop.hbase.client.Result; 042import org.apache.hadoop.hbase.client.ResultScanner; 043import org.apache.hadoop.hbase.client.Table; 044import org.apache.hadoop.hbase.client.TableDescriptorBuilder; 045import org.apache.hadoop.hbase.client.TableState; 046import org.apache.hadoop.hbase.constraint.ConstraintException; 047import org.apache.hadoop.hbase.exceptions.TimeoutIOException; 048import org.apache.hadoop.hbase.master.procedure.MasterProcedureEnv; 049import org.apache.hadoop.hbase.master.procedure.ProcedurePrepareLatch; 050import org.apache.hadoop.hbase.procedure2.ProcedureExecutor; 051import org.apache.hbase.thirdparty.com.google.common.collect.Sets; 052import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil; 053import org.apache.hadoop.hbase.shaded.protobuf.generated.HBaseProtos; 054import org.apache.hadoop.hbase.util.Bytes; 055import org.apache.hadoop.hbase.util.EnvironmentEdgeManager; 056import org.apache.hadoop.hbase.util.Threads; 057import org.apache.yetus.audience.InterfaceAudience; 058import org.slf4j.Logger; 059import org.slf4j.LoggerFactory; 060 061/** 062 * This is a helper class used internally to manage the namespace metadata that is stored in 063 * TableName.NAMESPACE_TABLE_NAME. It also mirrors updates to the ZK store by forwarding updates to 064 * {@link org.apache.hadoop.hbase.ZKNamespaceManager}. 065 * 066 * WARNING: Do not use. Go via the higher-level {@link ClusterSchema} API instead. This manager 067 * is likely to go aways anyways. 068 */ 069@InterfaceAudience.Private 070@edu.umd.cs.findbugs.annotations.SuppressWarnings(value="IS2_INCONSISTENT_SYNC", 071 justification="TODO: synchronize access on nsTable but it is done in tiers above and this " + 072 "class is going away/shrinking") 073public class TableNamespaceManager implements Stoppable { 074 private static final Logger LOG = LoggerFactory.getLogger(TableNamespaceManager.class); 075 private volatile boolean stopped = false; 076 077 private Configuration conf; 078 private MasterServices masterServices; 079 private Table nsTable = null; // FindBugs: IS2_INCONSISTENT_SYNC TODO: Access is not synchronized 080 private ZKNamespaceManager zkNamespaceManager; 081 private boolean initialized; 082 083 public static final String KEY_MAX_REGIONS = "hbase.namespace.quota.maxregions"; 084 public static final String KEY_MAX_TABLES = "hbase.namespace.quota.maxtables"; 085 static final String NS_INIT_TIMEOUT = "hbase.master.namespace.init.timeout"; 086 static final int DEFAULT_NS_INIT_TIMEOUT = 300000; 087 088 TableNamespaceManager(MasterServices masterServices) { 089 this.masterServices = masterServices; 090 this.conf = masterServices.getConfiguration(); 091 } 092 093 public void start() throws IOException { 094 if (!MetaTableAccessor.tableExists(masterServices.getConnection(), 095 TableName.NAMESPACE_TABLE_NAME)) { 096 LOG.info("Namespace table not found. Creating..."); 097 createNamespaceTable(masterServices); 098 } 099 100 try { 101 // Wait for the namespace table to be initialized. 102 long startTime = EnvironmentEdgeManager.currentTime(); 103 int timeout = conf.getInt(NS_INIT_TIMEOUT, DEFAULT_NS_INIT_TIMEOUT); 104 while (!isTableAvailableAndInitialized()) { 105 if (EnvironmentEdgeManager.currentTime() - startTime + 100 > timeout) { 106 // We can't do anything if ns is not online. 107 throw new IOException("Timedout " + timeout + "ms waiting for namespace table to " 108 + "be assigned and enabled: " + getTableState()); 109 } 110 Thread.sleep(100); 111 } 112 } catch (InterruptedException e) { 113 throw (InterruptedIOException) new InterruptedIOException().initCause(e); 114 } 115 } 116 117 private synchronized Table getNamespaceTable() throws IOException { 118 if (!isTableNamespaceManagerInitialized()) { 119 throw new IOException(this.getClass().getName() + " isn't ready to serve"); 120 } 121 return nsTable; 122 } 123 124 /* 125 * check whether a namespace has already existed. 126 */ 127 public boolean doesNamespaceExist(final String namespaceName) throws IOException { 128 if (nsTable == null) { 129 throw new IOException(this.getClass().getName() + " isn't ready to serve"); 130 } 131 return (get(nsTable, namespaceName) != null); 132 } 133 134 public synchronized NamespaceDescriptor get(String name) throws IOException { 135 if (!isTableNamespaceManagerInitialized()) { 136 return null; 137 } 138 return zkNamespaceManager.get(name); 139 } 140 141 private NamespaceDescriptor get(Table table, String name) throws IOException { 142 Result res = table.get(new Get(Bytes.toBytes(name))); 143 if (res.isEmpty()) { 144 return null; 145 } 146 byte[] val = CellUtil.cloneValue(res.getColumnLatestCell( 147 HTableDescriptor.NAMESPACE_FAMILY_INFO_BYTES, HTableDescriptor.NAMESPACE_COL_DESC_BYTES)); 148 return 149 ProtobufUtil.toNamespaceDescriptor( 150 HBaseProtos.NamespaceDescriptor.parseFrom(val)); 151 } 152 153 public void insertIntoNSTable(final NamespaceDescriptor ns) throws IOException { 154 if (nsTable == null) { 155 throw new IOException(this.getClass().getName() + " isn't ready to serve"); 156 } 157 byte[] row = Bytes.toBytes(ns.getName()); 158 Put p = new Put(row, true); 159 p.add(CellBuilderFactory.create(CellBuilderType.SHALLOW_COPY) 160 .setRow(row) 161 .setFamily(TableDescriptorBuilder.NAMESPACE_FAMILY_INFO_BYTES) 162 .setQualifier(TableDescriptorBuilder.NAMESPACE_COL_DESC_BYTES) 163 .setTimestamp(p.getTimestamp()) 164 .setType(Cell.Type.Put) 165 .setValue(ProtobufUtil.toProtoNamespaceDescriptor(ns).toByteArray()) 166 .build()); 167 nsTable.put(p); 168 } 169 170 public void updateZKNamespaceManager(final NamespaceDescriptor ns) throws IOException { 171 try { 172 zkNamespaceManager.update(ns); 173 } catch (IOException ex) { 174 String msg = "Failed to update namespace information in ZK."; 175 LOG.error(msg, ex); 176 throw new IOException(msg, ex); 177 } 178 } 179 180 public void removeFromNSTable(final String namespaceName) throws IOException { 181 if (nsTable == null) { 182 throw new IOException(this.getClass().getName() + " isn't ready to serve"); 183 } 184 Delete d = new Delete(Bytes.toBytes(namespaceName)); 185 nsTable.delete(d); 186 } 187 188 public void removeFromZKNamespaceManager(final String namespaceName) throws IOException { 189 zkNamespaceManager.remove(namespaceName); 190 } 191 192 public synchronized NavigableSet<NamespaceDescriptor> list() throws IOException { 193 NavigableSet<NamespaceDescriptor> ret = 194 Sets.newTreeSet(NamespaceDescriptor.NAMESPACE_DESCRIPTOR_COMPARATOR); 195 ResultScanner scanner = 196 getNamespaceTable().getScanner(HTableDescriptor.NAMESPACE_FAMILY_INFO_BYTES); 197 try { 198 for(Result r : scanner) { 199 byte[] val = CellUtil.cloneValue(r.getColumnLatestCell( 200 HTableDescriptor.NAMESPACE_FAMILY_INFO_BYTES, 201 HTableDescriptor.NAMESPACE_COL_DESC_BYTES)); 202 ret.add(ProtobufUtil.toNamespaceDescriptor( 203 HBaseProtos.NamespaceDescriptor.parseFrom(val))); 204 } 205 } finally { 206 scanner.close(); 207 } 208 return ret; 209 } 210 211 private void createNamespaceTable(MasterServices masterServices) throws IOException { 212 masterServices.createSystemTable(HTableDescriptor.NAMESPACE_TABLEDESC); 213 } 214 215 @SuppressWarnings("deprecation") 216 private boolean isTableNamespaceManagerInitialized() throws IOException { 217 if (initialized) { 218 this.nsTable = this.masterServices.getConnection().getTable(TableName.NAMESPACE_TABLE_NAME); 219 return true; 220 } 221 return false; 222 } 223 224 /** 225 * Create Namespace in a blocking manner. Keeps trying until 226 * {@link ClusterSchema#HBASE_MASTER_CLUSTER_SCHEMA_OPERATION_TIMEOUT_KEY} expires. 227 * Note, by-passes notifying coprocessors and name checks. Use for system namespaces only. 228 */ 229 private void blockingCreateNamespace(final NamespaceDescriptor namespaceDescriptor) 230 throws IOException { 231 ClusterSchema clusterSchema = this.masterServices.getClusterSchema(); 232 long procId = clusterSchema.createNamespace(namespaceDescriptor, null, ProcedurePrepareLatch.getNoopLatch()); 233 block(this.masterServices, procId); 234 } 235 236 237 /** 238 * An ugly utility to be removed when refactor TableNamespaceManager. 239 * @throws TimeoutIOException 240 */ 241 private static void block(final MasterServices services, final long procId) 242 throws TimeoutIOException { 243 int timeoutInMillis = services.getConfiguration(). 244 getInt(ClusterSchema.HBASE_MASTER_CLUSTER_SCHEMA_OPERATION_TIMEOUT_KEY, 245 ClusterSchema.DEFAULT_HBASE_MASTER_CLUSTER_SCHEMA_OPERATION_TIMEOUT); 246 long deadlineTs = EnvironmentEdgeManager.currentTime() + timeoutInMillis; 247 ProcedureExecutor<MasterProcedureEnv> procedureExecutor = 248 services.getMasterProcedureExecutor(); 249 while(EnvironmentEdgeManager.currentTime() < deadlineTs) { 250 if (procedureExecutor.isFinished(procId)) return; 251 // Sleep some 252 Threads.sleep(10); 253 } 254 throw new TimeoutIOException("Procedure pid=" + procId + " is still running"); 255 } 256 257 /** 258 * This method checks if the namespace table is assigned and then 259 * tries to create its Table reference. If it was already created before, it also makes 260 * sure that the connection isn't closed. 261 * @return true if the namespace table manager is ready to serve, false otherwise 262 */ 263 @SuppressWarnings("deprecation") 264 public synchronized boolean isTableAvailableAndInitialized() 265 throws IOException { 266 // Did we already get a table? If so, still make sure it's available 267 if (isTableNamespaceManagerInitialized()) { 268 return true; 269 } 270 271 // Now check if the table is assigned, if not then fail fast 272 if (isTableAssigned() && isTableEnabled()) { 273 try { 274 boolean initGoodSofar = true; 275 nsTable = this.masterServices.getConnection().getTable(TableName.NAMESPACE_TABLE_NAME); 276 zkNamespaceManager = new ZKNamespaceManager(masterServices.getZooKeeper()); 277 zkNamespaceManager.start(); 278 279 if (get(nsTable, NamespaceDescriptor.DEFAULT_NAMESPACE.getName()) == null) { 280 blockingCreateNamespace(NamespaceDescriptor.DEFAULT_NAMESPACE); 281 } 282 if (get(nsTable, NamespaceDescriptor.SYSTEM_NAMESPACE.getName()) == null) { 283 blockingCreateNamespace(NamespaceDescriptor.SYSTEM_NAMESPACE); 284 } 285 286 if (!initGoodSofar) { 287 // some required namespace is created asynchronized. We should complete init later. 288 return false; 289 } 290 291 ResultScanner scanner = nsTable.getScanner(HTableDescriptor.NAMESPACE_FAMILY_INFO_BYTES); 292 try { 293 for (Result result : scanner) { 294 byte[] val = CellUtil.cloneValue(result.getColumnLatestCell( 295 HTableDescriptor.NAMESPACE_FAMILY_INFO_BYTES, 296 HTableDescriptor.NAMESPACE_COL_DESC_BYTES)); 297 NamespaceDescriptor ns = 298 ProtobufUtil.toNamespaceDescriptor( 299 HBaseProtos.NamespaceDescriptor.parseFrom(val)); 300 zkNamespaceManager.update(ns); 301 } 302 } finally { 303 scanner.close(); 304 } 305 initialized = true; 306 return true; 307 } catch (IOException ie) { 308 LOG.warn("Caught exception in initializing namespace table manager", ie); 309 if (nsTable != null) { 310 nsTable.close(); 311 } 312 throw ie; 313 } 314 } 315 return false; 316 } 317 318 private TableState getTableState() throws IOException { 319 return masterServices.getTableStateManager().getTableState(TableName.NAMESPACE_TABLE_NAME); 320 } 321 322 private boolean isTableEnabled() throws IOException { 323 return getTableState().isEnabled(); 324 } 325 326 private boolean isTableAssigned() { 327 // TODO: we have a better way now (wait on event) 328 return masterServices.getAssignmentManager() 329 .getRegionStates().hasTableRegionStates(TableName.NAMESPACE_TABLE_NAME); 330 } 331 332 public void validateTableAndRegionCount(NamespaceDescriptor desc) throws IOException { 333 if (getMaxRegions(desc) <= 0) { 334 throw new ConstraintException("The max region quota for " + desc.getName() 335 + " is less than or equal to zero."); 336 } 337 if (getMaxTables(desc) <= 0) { 338 throw new ConstraintException("The max tables quota for " + desc.getName() 339 + " is less than or equal to zero."); 340 } 341 } 342 343 public static long getMaxTables(NamespaceDescriptor ns) throws IOException { 344 String value = ns.getConfigurationValue(KEY_MAX_TABLES); 345 long maxTables = 0; 346 if (StringUtils.isNotEmpty(value)) { 347 try { 348 maxTables = Long.parseLong(value); 349 } catch (NumberFormatException exp) { 350 throw new DoNotRetryIOException("NumberFormatException while getting max tables.", exp); 351 } 352 } else { 353 // The property is not set, so assume its the max long value. 354 maxTables = Long.MAX_VALUE; 355 } 356 return maxTables; 357 } 358 359 public static long getMaxRegions(NamespaceDescriptor ns) throws IOException { 360 String value = ns.getConfigurationValue(KEY_MAX_REGIONS); 361 long maxRegions = 0; 362 if (StringUtils.isNotEmpty(value)) { 363 try { 364 maxRegions = Long.parseLong(value); 365 } catch (NumberFormatException exp) { 366 throw new DoNotRetryIOException("NumberFormatException while getting max regions.", exp); 367 } 368 } else { 369 // The property is not set, so assume its the max long value. 370 maxRegions = Long.MAX_VALUE; 371 } 372 return maxRegions; 373 } 374 375 @Override 376 public boolean isStopped() { 377 return this.stopped; 378 } 379 380 @Override 381 public void stop(String why) { 382 if (this.stopped) { 383 return; 384 } 385 try { 386 if (this.zkNamespaceManager != null) { 387 this.zkNamespaceManager.stop(); 388 } 389 } catch (IOException ioe) { 390 LOG.warn("Failed NamespaceManager close", ioe); 391 } 392 try { 393 if (this.nsTable != null) { 394 this.nsTable.close(); 395 } 396 } catch (IOException ioe) { 397 LOG.warn("Failed Namespace Table close", ioe); 398 } 399 this.stopped = true; 400 } 401}