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