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 */ 018package org.apache.hadoop.hbase.master.procedure; 019 020import java.io.IOException; 021import org.apache.hadoop.hbase.NamespaceDescriptor; 022import org.apache.hadoop.hbase.NamespaceExistException; 023import org.apache.hadoop.hbase.master.MasterFileSystem; 024import org.apache.hadoop.hbase.master.TableNamespaceManager; 025import org.apache.hadoop.hbase.procedure2.ProcedureStateSerializer; 026import org.apache.hadoop.hbase.util.CommonFSUtils; 027import org.apache.yetus.audience.InterfaceAudience; 028import org.slf4j.Logger; 029import org.slf4j.LoggerFactory; 030 031import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil; 032import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProcedureProtos; 033import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProcedureProtos.CreateNamespaceState; 034 035/** 036 * The procedure to create a new namespace. 037 */ 038@InterfaceAudience.Private 039public class CreateNamespaceProcedure 040 extends AbstractStateMachineNamespaceProcedure<CreateNamespaceState> { 041 private static final Logger LOG = LoggerFactory.getLogger(CreateNamespaceProcedure.class); 042 043 private NamespaceDescriptor nsDescriptor; 044 private Boolean traceEnabled; 045 046 public CreateNamespaceProcedure() { 047 this.traceEnabled = null; 048 } 049 050 public CreateNamespaceProcedure(final MasterProcedureEnv env, 051 final NamespaceDescriptor nsDescriptor) { 052 this(env, nsDescriptor, null); 053 } 054 055 public CreateNamespaceProcedure(final MasterProcedureEnv env, 056 final NamespaceDescriptor nsDescriptor, ProcedurePrepareLatch latch) { 057 super(env, latch); 058 this.nsDescriptor = nsDescriptor; 059 this.traceEnabled = null; 060 } 061 062 @Override 063 protected Flow executeFromState(final MasterProcedureEnv env, final CreateNamespaceState state) 064 throws InterruptedException { 065 if (isTraceEnabled()) { 066 LOG.trace(this + " execute state=" + state); 067 } 068 try { 069 switch (state) { 070 case CREATE_NAMESPACE_PREPARE: 071 boolean success = prepareCreate(env); 072 releaseSyncLatch(); 073 if (!success) { 074 assert isFailed() : "createNamespace should have an exception here"; 075 return Flow.NO_MORE_STATE; 076 } 077 setNextState(CreateNamespaceState.CREATE_NAMESPACE_CREATE_DIRECTORY); 078 break; 079 case CREATE_NAMESPACE_CREATE_DIRECTORY: 080 createDirectory(env, nsDescriptor); 081 setNextState(CreateNamespaceState.CREATE_NAMESPACE_INSERT_INTO_NS_TABLE); 082 break; 083 case CREATE_NAMESPACE_INSERT_INTO_NS_TABLE: 084 insertIntoNSTable(env, nsDescriptor); 085 setNextState(CreateNamespaceState.CREATE_NAMESPACE_UPDATE_ZK); 086 break; 087 case CREATE_NAMESPACE_UPDATE_ZK: 088 updateZKNamespaceManager(env, nsDescriptor); 089 setNextState(CreateNamespaceState.CREATE_NAMESPACE_SET_NAMESPACE_QUOTA); 090 break; 091 case CREATE_NAMESPACE_SET_NAMESPACE_QUOTA: 092 setNamespaceQuota(env, nsDescriptor); 093 return Flow.NO_MORE_STATE; 094 default: 095 throw new UnsupportedOperationException(this + " unhandled state=" + state); 096 } 097 } catch (IOException e) { 098 if (isRollbackSupported(state)) { 099 setFailure("master-create-namespace", e); 100 } else { 101 LOG.warn("Retriable error trying to create namespace=" + nsDescriptor.getName() 102 + " (in state=" + state + ")", e); 103 } 104 } 105 return Flow.HAS_MORE_STATE; 106 } 107 108 @Override 109 protected void rollbackState(final MasterProcedureEnv env, final CreateNamespaceState state) 110 throws IOException { 111 if (state == CreateNamespaceState.CREATE_NAMESPACE_PREPARE) { 112 // nothing to rollback, pre-create is just state checks. 113 // TODO: coprocessor rollback semantic is still undefined. 114 releaseSyncLatch(); 115 return; 116 } 117 // The procedure doesn't have a rollback. The execution will succeed, at some point. 118 throw new UnsupportedOperationException("unhandled state=" + state); 119 } 120 121 @Override 122 protected boolean isRollbackSupported(final CreateNamespaceState state) { 123 switch (state) { 124 case CREATE_NAMESPACE_PREPARE: 125 return true; 126 default: 127 return false; 128 } 129 } 130 131 @Override 132 protected CreateNamespaceState getState(final int stateId) { 133 return CreateNamespaceState.forNumber(stateId); 134 } 135 136 @Override 137 protected int getStateId(final CreateNamespaceState state) { 138 return state.getNumber(); 139 } 140 141 @Override 142 protected CreateNamespaceState getInitialState() { 143 return CreateNamespaceState.CREATE_NAMESPACE_PREPARE; 144 } 145 146 @Override 147 protected void serializeStateData(ProcedureStateSerializer serializer) throws IOException { 148 super.serializeStateData(serializer); 149 150 MasterProcedureProtos.CreateNamespaceStateData.Builder createNamespaceMsg = 151 MasterProcedureProtos.CreateNamespaceStateData.newBuilder() 152 .setNamespaceDescriptor(ProtobufUtil.toProtoNamespaceDescriptor(this.nsDescriptor)); 153 serializer.serialize(createNamespaceMsg.build()); 154 } 155 156 @Override 157 protected void deserializeStateData(ProcedureStateSerializer serializer) throws IOException { 158 super.deserializeStateData(serializer); 159 160 MasterProcedureProtos.CreateNamespaceStateData createNamespaceMsg = 161 serializer.deserialize(MasterProcedureProtos.CreateNamespaceStateData.class); 162 nsDescriptor = ProtobufUtil.toNamespaceDescriptor(createNamespaceMsg.getNamespaceDescriptor()); 163 } 164 165 private boolean isBootstrapNamespace() { 166 return nsDescriptor.equals(NamespaceDescriptor.DEFAULT_NAMESPACE) 167 || nsDescriptor.equals(NamespaceDescriptor.SYSTEM_NAMESPACE); 168 } 169 170 @Override 171 protected boolean waitInitialized(MasterProcedureEnv env) { 172 // Namespace manager might not be ready if master is not fully initialized, 173 // return false to reject user namespace creation; return true for default 174 // and system namespace creation (this is part of master initialization). 175 if (isBootstrapNamespace()) { 176 return false; 177 } 178 return env.waitInitialized(this); 179 } 180 181 @Override 182 protected LockState acquireLock(final MasterProcedureEnv env) { 183 if (env.getProcedureScheduler().waitNamespaceExclusiveLock(this, getNamespaceName())) { 184 return LockState.LOCK_EVENT_WAIT; 185 } 186 return LockState.LOCK_ACQUIRED; 187 } 188 189 @Override 190 public TableOperationType getTableOperationType() { 191 return TableOperationType.EDIT; 192 } 193 194 @Override 195 protected String getNamespaceName() { 196 return nsDescriptor.getName(); 197 } 198 199 /** 200 * Action before any real action of creating namespace. 201 * @param env MasterProcedureEnv 202 */ 203 private boolean prepareCreate(final MasterProcedureEnv env) throws IOException { 204 if (getTableNamespaceManager(env).doesNamespaceExist(nsDescriptor.getName())) { 205 setFailure("master-create-namespace", 206 new NamespaceExistException("Namespace " + nsDescriptor.getName() + " already exists")); 207 return false; 208 } 209 getTableNamespaceManager(env).validateTableAndRegionCount(nsDescriptor); 210 return true; 211 } 212 213 /** 214 * Create the namespace directory 215 * @param env MasterProcedureEnv 216 * @param nsDescriptor NamespaceDescriptor 217 */ 218 protected static void createDirectory(final MasterProcedureEnv env, 219 final NamespaceDescriptor nsDescriptor) throws IOException { 220 MasterFileSystem mfs = env.getMasterServices().getMasterFileSystem(); 221 mfs.getFileSystem() 222 .mkdirs(CommonFSUtils.getNamespaceDir(mfs.getRootDir(), nsDescriptor.getName())); 223 } 224 225 /** 226 * Insert the row into ns table 227 * @param env MasterProcedureEnv 228 * @param nsDescriptor NamespaceDescriptor 229 */ 230 protected static void insertIntoNSTable(final MasterProcedureEnv env, 231 final NamespaceDescriptor nsDescriptor) throws IOException { 232 getTableNamespaceManager(env).insertIntoNSTable(nsDescriptor); 233 } 234 235 /** 236 * Update ZooKeeper. 237 * @param env MasterProcedureEnv 238 * @param nsDescriptor NamespaceDescriptor 239 */ 240 protected static void updateZKNamespaceManager(final MasterProcedureEnv env, 241 final NamespaceDescriptor nsDescriptor) throws IOException { 242 getTableNamespaceManager(env).updateZKNamespaceManager(nsDescriptor); 243 } 244 245 /** 246 * Set quota for the namespace 247 * @param env MasterProcedureEnv 248 * @param nsDescriptor NamespaceDescriptor 249 **/ 250 protected static void setNamespaceQuota(final MasterProcedureEnv env, 251 final NamespaceDescriptor nsDescriptor) throws IOException { 252 if (env.getMasterServices().isInitialized()) { 253 env.getMasterServices().getMasterQuotaManager().setNamespaceQuota(nsDescriptor); 254 } 255 } 256 257 private static TableNamespaceManager getTableNamespaceManager(final MasterProcedureEnv env) { 258 return env.getMasterServices().getClusterSchema().getTableNamespaceManager(); 259 } 260 261 /** 262 * The procedure could be restarted from a different machine. If the variable is null, we need to 263 * retrieve it. 264 */ 265 private Boolean isTraceEnabled() { 266 if (traceEnabled == null) { 267 traceEnabled = LOG.isTraceEnabled(); 268 } 269 return traceEnabled; 270 } 271 272 @Override 273 protected boolean shouldWaitClientAck(MasterProcedureEnv env) { 274 // hbase and default namespaces are created on bootstrap internally by the system 275 // the client does not know about this procedures. 276 return !isBootstrapNamespace(); 277 } 278}