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; 024import org.apache.commons.lang3.StringUtils; 025import org.apache.hadoop.conf.Configuration; 026import org.apache.hadoop.hbase.Cell; 027import org.apache.hadoop.hbase.CellBuilderFactory; 028import org.apache.hadoop.hbase.CellBuilderType; 029import org.apache.hadoop.hbase.CellUtil; 030import org.apache.hadoop.hbase.DoNotRetryIOException; 031import org.apache.hadoop.hbase.HTableDescriptor; 032import org.apache.hadoop.hbase.NamespaceDescriptor; 033import org.apache.hadoop.hbase.Stoppable; 034import org.apache.hadoop.hbase.TableName; 035import org.apache.hadoop.hbase.ZKNamespaceManager; 036import org.apache.hadoop.hbase.client.Delete; 037import org.apache.hadoop.hbase.client.Get; 038import org.apache.hadoop.hbase.client.Put; 039import org.apache.hadoop.hbase.client.Result; 040import org.apache.hadoop.hbase.client.ResultScanner; 041import org.apache.hadoop.hbase.client.Table; 042import org.apache.hadoop.hbase.client.TableDescriptorBuilder; 043import org.apache.hadoop.hbase.client.TableState; 044import org.apache.hadoop.hbase.constraint.ConstraintException; 045import org.apache.hadoop.hbase.exceptions.TimeoutIOException; 046import org.apache.hadoop.hbase.master.procedure.MasterProcedureEnv; 047import org.apache.hadoop.hbase.master.procedure.ProcedurePrepareLatch; 048import org.apache.hadoop.hbase.procedure2.ProcedureExecutor; 049import org.apache.hadoop.hbase.util.Bytes; 050import org.apache.hadoop.hbase.util.EnvironmentEdgeManager; 051import org.apache.hadoop.hbase.util.Threads; 052import org.apache.yetus.audience.InterfaceAudience; 053import org.slf4j.Logger; 054import org.slf4j.LoggerFactory; 055 056import org.apache.hbase.thirdparty.com.google.common.collect.Sets; 057 058import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil; 059import org.apache.hadoop.hbase.shaded.protobuf.generated.HBaseProtos; 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 (!masterServices.getTableDescriptors().exists(TableName.NAMESPACE_TABLE_NAME)) { 095 LOG.info("Namespace table not found. Creating..."); 096 createNamespaceTable(masterServices); 097 } 098 099 try { 100 // Wait for the namespace table to be initialized. 101 long startTime = EnvironmentEdgeManager.currentTime(); 102 int timeout = conf.getInt(NS_INIT_TIMEOUT, DEFAULT_NS_INIT_TIMEOUT); 103 while (!isTableAvailableAndInitialized()) { 104 if (EnvironmentEdgeManager.currentTime() - startTime + 100 > timeout) { 105 // We can't do anything if ns is not online. 106 throw new IOException("Timedout " + timeout + "ms waiting for namespace table to " 107 + "be assigned and enabled: " + getTableState()); 108 } 109 Thread.sleep(100); 110 } 111 } catch (InterruptedException e) { 112 throw (InterruptedIOException) new InterruptedIOException().initCause(e); 113 } 114 } 115 116 private synchronized Table getNamespaceTable() throws IOException { 117 if (!isTableNamespaceManagerInitialized()) { 118 throw new IOException(this.getClass().getName() + " isn't ready to serve"); 119 } 120 return nsTable; 121 } 122 123 /* 124 * check whether a namespace has already existed. 125 */ 126 public boolean doesNamespaceExist(final String namespaceName) throws IOException { 127 if (nsTable == null) { 128 throw new IOException(this.getClass().getName() + " isn't ready to serve"); 129 } 130 return (get(nsTable, namespaceName) != null); 131 } 132 133 public synchronized NamespaceDescriptor get(String name) throws IOException { 134 if (!isTableNamespaceManagerInitialized()) { 135 return null; 136 } 137 return zkNamespaceManager.get(name); 138 } 139 140 private NamespaceDescriptor get(Table table, String name) throws IOException { 141 Result res = table.get(new Get(Bytes.toBytes(name))); 142 if (res.isEmpty()) { 143 return null; 144 } 145 byte[] val = CellUtil.cloneValue(res.getColumnLatestCell( 146 HTableDescriptor.NAMESPACE_FAMILY_INFO_BYTES, HTableDescriptor.NAMESPACE_COL_DESC_BYTES)); 147 return 148 ProtobufUtil.toNamespaceDescriptor( 149 HBaseProtos.NamespaceDescriptor.parseFrom(val)); 150 } 151 152 public void insertIntoNSTable(final NamespaceDescriptor ns) throws IOException { 153 if (nsTable == null) { 154 throw new IOException(this.getClass().getName() + " isn't ready to serve"); 155 } 156 byte[] row = Bytes.toBytes(ns.getName()); 157 Put p = new Put(row, true); 158 p.add(CellBuilderFactory.create(CellBuilderType.SHALLOW_COPY) 159 .setRow(row) 160 .setFamily(TableDescriptorBuilder.NAMESPACE_FAMILY_INFO_BYTES) 161 .setQualifier(TableDescriptorBuilder.NAMESPACE_COL_DESC_BYTES) 162 .setTimestamp(p.getTimestamp()) 163 .setType(Cell.Type.Put) 164 .setValue(ProtobufUtil.toProtoNamespaceDescriptor(ns).toByteArray()) 165 .build()); 166 nsTable.put(p); 167 } 168 169 public void updateZKNamespaceManager(final NamespaceDescriptor ns) throws IOException { 170 try { 171 zkNamespaceManager.update(ns); 172 } catch (IOException ex) { 173 String msg = "Failed to update namespace information in ZK."; 174 LOG.error(msg, ex); 175 throw new IOException(msg, ex); 176 } 177 } 178 179 public void removeFromNSTable(final String namespaceName) throws IOException { 180 if (nsTable == null) { 181 throw new IOException(this.getClass().getName() + " isn't ready to serve"); 182 } 183 Delete d = new Delete(Bytes.toBytes(namespaceName)); 184 nsTable.delete(d); 185 } 186 187 public void removeFromZKNamespaceManager(final String namespaceName) throws IOException { 188 zkNamespaceManager.remove(namespaceName); 189 } 190 191 public synchronized NavigableSet<NamespaceDescriptor> list() throws IOException { 192 NavigableSet<NamespaceDescriptor> ret = 193 Sets.newTreeSet(NamespaceDescriptor.NAMESPACE_DESCRIPTOR_COMPARATOR); 194 ResultScanner scanner = 195 getNamespaceTable().getScanner(HTableDescriptor.NAMESPACE_FAMILY_INFO_BYTES); 196 try { 197 for(Result r : scanner) { 198 byte[] val = CellUtil.cloneValue(r.getColumnLatestCell( 199 HTableDescriptor.NAMESPACE_FAMILY_INFO_BYTES, 200 HTableDescriptor.NAMESPACE_COL_DESC_BYTES)); 201 ret.add(ProtobufUtil.toNamespaceDescriptor( 202 HBaseProtos.NamespaceDescriptor.parseFrom(val))); 203 } 204 } finally { 205 scanner.close(); 206 } 207 return ret; 208 } 209 210 private void createNamespaceTable(MasterServices masterServices) throws IOException { 211 masterServices.createSystemTable(HTableDescriptor.NAMESPACE_TABLEDESC); 212 } 213 214 @SuppressWarnings("deprecation") 215 private boolean isTableNamespaceManagerInitialized() throws IOException { 216 if (initialized) { 217 this.nsTable = this.masterServices.getConnection().getTable(TableName.NAMESPACE_TABLE_NAME); 218 return true; 219 } 220 return false; 221 } 222 223 /** 224 * Create Namespace in a blocking manner. Keeps trying until 225 * {@link ClusterSchema#HBASE_MASTER_CLUSTER_SCHEMA_OPERATION_TIMEOUT_KEY} expires. 226 * Note, by-passes notifying coprocessors and name checks. Use for system namespaces only. 227 */ 228 private void blockingCreateNamespace(final NamespaceDescriptor namespaceDescriptor) 229 throws IOException { 230 ClusterSchema clusterSchema = this.masterServices.getClusterSchema(); 231 long procId = clusterSchema.createNamespace(namespaceDescriptor, null, ProcedurePrepareLatch.getNoopLatch()); 232 block(this.masterServices, procId); 233 } 234 235 236 /** 237 * An ugly utility to be removed when refactor TableNamespaceManager. 238 * @throws TimeoutIOException 239 */ 240 private static void block(final MasterServices services, final long procId) 241 throws TimeoutIOException { 242 int timeoutInMillis = services.getConfiguration(). 243 getInt(ClusterSchema.HBASE_MASTER_CLUSTER_SCHEMA_OPERATION_TIMEOUT_KEY, 244 ClusterSchema.DEFAULT_HBASE_MASTER_CLUSTER_SCHEMA_OPERATION_TIMEOUT); 245 long deadlineTs = EnvironmentEdgeManager.currentTime() + timeoutInMillis; 246 ProcedureExecutor<MasterProcedureEnv> procedureExecutor = 247 services.getMasterProcedureExecutor(); 248 while(EnvironmentEdgeManager.currentTime() < deadlineTs) { 249 if (procedureExecutor.isFinished(procId)) return; 250 // Sleep some 251 Threads.sleep(10); 252 } 253 throw new TimeoutIOException("Procedure pid=" + procId + " is still running"); 254 } 255 256 /** 257 * This method checks if the namespace table is assigned and then 258 * tries to create its Table reference. If it was already created before, it also makes 259 * sure that the connection isn't closed. 260 * @return true if the namespace table manager is ready to serve, false otherwise 261 */ 262 @SuppressWarnings("deprecation") 263 public synchronized boolean isTableAvailableAndInitialized() 264 throws IOException { 265 // Did we already get a table? If so, still make sure it's available 266 if (isTableNamespaceManagerInitialized()) { 267 return true; 268 } 269 270 // Now check if the table is assigned, if not then fail fast 271 if (isTableAssigned() && isTableEnabled()) { 272 try { 273 boolean initGoodSofar = true; 274 nsTable = this.masterServices.getConnection().getTable(TableName.NAMESPACE_TABLE_NAME); 275 zkNamespaceManager = new ZKNamespaceManager(masterServices.getZooKeeper()); 276 zkNamespaceManager.start(); 277 278 if (get(nsTable, NamespaceDescriptor.DEFAULT_NAMESPACE.getName()) == null) { 279 blockingCreateNamespace(NamespaceDescriptor.DEFAULT_NAMESPACE); 280 } 281 if (get(nsTable, NamespaceDescriptor.SYSTEM_NAMESPACE.getName()) == null) { 282 blockingCreateNamespace(NamespaceDescriptor.SYSTEM_NAMESPACE); 283 } 284 285 if (!initGoodSofar) { 286 // some required namespace is created asynchronized. We should complete init later. 287 return false; 288 } 289 290 ResultScanner scanner = nsTable.getScanner(HTableDescriptor.NAMESPACE_FAMILY_INFO_BYTES); 291 try { 292 for (Result result : scanner) { 293 byte[] val = CellUtil.cloneValue(result.getColumnLatestCell( 294 HTableDescriptor.NAMESPACE_FAMILY_INFO_BYTES, 295 HTableDescriptor.NAMESPACE_COL_DESC_BYTES)); 296 NamespaceDescriptor ns = 297 ProtobufUtil.toNamespaceDescriptor( 298 HBaseProtos.NamespaceDescriptor.parseFrom(val)); 299 zkNamespaceManager.update(ns); 300 } 301 } finally { 302 scanner.close(); 303 } 304 initialized = true; 305 return true; 306 } catch (IOException ie) { 307 LOG.warn("Caught exception in initializing namespace table manager", ie); 308 if (nsTable != null) { 309 nsTable.close(); 310 } 311 throw ie; 312 } 313 } 314 return false; 315 } 316 317 private TableState getTableState() throws IOException { 318 return masterServices.getTableStateManager().getTableState(TableName.NAMESPACE_TABLE_NAME); 319 } 320 321 private boolean isTableEnabled() throws IOException { 322 return getTableState().isEnabled(); 323 } 324 325 private boolean isTableAssigned() { 326 // TODO: we have a better way now (wait on event) 327 return masterServices.getAssignmentManager() 328 .getRegionStates().hasTableRegionStates(TableName.NAMESPACE_TABLE_NAME); 329 } 330 331 public void validateTableAndRegionCount(NamespaceDescriptor desc) throws IOException { 332 if (getMaxRegions(desc) <= 0) { 333 throw new ConstraintException("The max region quota for " + desc.getName() 334 + " is less than or equal to zero."); 335 } 336 if (getMaxTables(desc) <= 0) { 337 throw new ConstraintException("The max tables quota for " + desc.getName() 338 + " is less than or equal to zero."); 339 } 340 } 341 342 public static long getMaxTables(NamespaceDescriptor ns) throws IOException { 343 String value = ns.getConfigurationValue(KEY_MAX_TABLES); 344 long maxTables = 0; 345 if (StringUtils.isNotEmpty(value)) { 346 try { 347 maxTables = Long.parseLong(value); 348 } catch (NumberFormatException exp) { 349 throw new DoNotRetryIOException("NumberFormatException while getting max tables.", exp); 350 } 351 } else { 352 // The property is not set, so assume its the max long value. 353 maxTables = Long.MAX_VALUE; 354 } 355 return maxTables; 356 } 357 358 public static long getMaxRegions(NamespaceDescriptor ns) throws IOException { 359 String value = ns.getConfigurationValue(KEY_MAX_REGIONS); 360 long maxRegions = 0; 361 if (StringUtils.isNotEmpty(value)) { 362 try { 363 maxRegions = Long.parseLong(value); 364 } catch (NumberFormatException exp) { 365 throw new DoNotRetryIOException("NumberFormatException while getting max regions.", exp); 366 } 367 } else { 368 // The property is not set, so assume its the max long value. 369 maxRegions = Long.MAX_VALUE; 370 } 371 return maxRegions; 372 } 373 374 @Override 375 public boolean isStopped() { 376 return this.stopped; 377 } 378 379 @Override 380 public void stop(String why) { 381 if (this.stopped) { 382 return; 383 } 384 try { 385 if (this.zkNamespaceManager != null) { 386 this.zkNamespaceManager.stop(); 387 } 388 } catch (IOException ioe) { 389 LOG.warn("Failed NamespaceManager close", ioe); 390 } 391 try { 392 if (this.nsTable != null) { 393 this.nsTable.close(); 394 } 395 } catch (IOException ioe) { 396 LOG.warn("Failed Namespace Table close", ioe); 397 } 398 this.stopped = true; 399 } 400}