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.procedure2.ProcedureStateSerializer;
024import org.apache.yetus.audience.InterfaceAudience;
025import org.slf4j.Logger;
026import org.slf4j.LoggerFactory;
027
028import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil;
029import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProcedureProtos;
030import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProcedureProtos.CreateNamespaceState;
031
032/**
033 * The procedure to create a new namespace.
034 */
035@InterfaceAudience.Private
036public class CreateNamespaceProcedure
037  extends AbstractStateMachineNamespaceProcedure<CreateNamespaceState> {
038  private static final Logger LOG = LoggerFactory.getLogger(CreateNamespaceProcedure.class);
039
040  private NamespaceDescriptor nsDescriptor;
041
042  public CreateNamespaceProcedure() {
043  }
044
045  public CreateNamespaceProcedure(final MasterProcedureEnv env,
046    final NamespaceDescriptor nsDescriptor) {
047    this(env, nsDescriptor, null);
048  }
049
050  public CreateNamespaceProcedure(final MasterProcedureEnv env,
051    final NamespaceDescriptor nsDescriptor, ProcedurePrepareLatch latch) {
052    super(env, latch);
053    this.nsDescriptor = nsDescriptor;
054  }
055
056  @Override
057  protected Flow executeFromState(final MasterProcedureEnv env, final CreateNamespaceState state)
058    throws InterruptedException {
059    LOG.trace("{} execute state={}", this, state);
060    try {
061      switch (state) {
062        case CREATE_NAMESPACE_PREPARE:
063          boolean success = prepareCreate(env);
064          releaseSyncLatch();
065          if (!success) {
066            assert isFailed() : "createNamespace should have an exception here";
067            return Flow.NO_MORE_STATE;
068          }
069          setNextState(CreateNamespaceState.CREATE_NAMESPACE_CREATE_DIRECTORY);
070          break;
071        case CREATE_NAMESPACE_CREATE_DIRECTORY:
072          createDirectory(env, nsDescriptor);
073          setNextState(CreateNamespaceState.CREATE_NAMESPACE_INSERT_INTO_NS_TABLE);
074          break;
075        case CREATE_NAMESPACE_INSERT_INTO_NS_TABLE:
076          addOrUpdateNamespace(env, nsDescriptor);
077          setNextState(CreateNamespaceState.CREATE_NAMESPACE_SET_NAMESPACE_QUOTA);
078          break;
079        case CREATE_NAMESPACE_UPDATE_ZK:
080          // not used any more
081          setNextState(CreateNamespaceState.CREATE_NAMESPACE_SET_NAMESPACE_QUOTA);
082          break;
083        case CREATE_NAMESPACE_SET_NAMESPACE_QUOTA:
084          setNamespaceQuota(env, nsDescriptor);
085          return Flow.NO_MORE_STATE;
086        default:
087          throw new UnsupportedOperationException(this + " unhandled state=" + state);
088      }
089    } catch (IOException e) {
090      if (isRollbackSupported(state)) {
091        setFailure("master-create-namespace", e);
092      } else {
093        LOG.warn("Retriable error trying to create namespace=" + nsDescriptor.getName()
094          + " (in state=" + state + ")", e);
095      }
096    }
097    return Flow.HAS_MORE_STATE;
098  }
099
100  @Override
101  protected void rollbackState(final MasterProcedureEnv env, final CreateNamespaceState state)
102    throws IOException {
103    if (state == CreateNamespaceState.CREATE_NAMESPACE_PREPARE) {
104      // nothing to rollback, pre-create is just state checks.
105      // TODO: coprocessor rollback semantic is still undefined.
106      releaseSyncLatch();
107      return;
108    }
109    // The procedure doesn't have a rollback. The execution will succeed, at some point.
110    throw new UnsupportedOperationException("unhandled state=" + state);
111  }
112
113  @Override
114  protected boolean isRollbackSupported(final CreateNamespaceState state) {
115    switch (state) {
116      case CREATE_NAMESPACE_PREPARE:
117        return true;
118      default:
119        return false;
120    }
121  }
122
123  @Override
124  protected CreateNamespaceState getState(final int stateId) {
125    return CreateNamespaceState.forNumber(stateId);
126  }
127
128  @Override
129  protected int getStateId(final CreateNamespaceState state) {
130    return state.getNumber();
131  }
132
133  @Override
134  protected CreateNamespaceState getInitialState() {
135    return CreateNamespaceState.CREATE_NAMESPACE_PREPARE;
136  }
137
138  @Override
139  protected void serializeStateData(ProcedureStateSerializer serializer) throws IOException {
140    super.serializeStateData(serializer);
141
142    MasterProcedureProtos.CreateNamespaceStateData.Builder createNamespaceMsg =
143      MasterProcedureProtos.CreateNamespaceStateData.newBuilder()
144        .setNamespaceDescriptor(ProtobufUtil.toProtoNamespaceDescriptor(this.nsDescriptor));
145    serializer.serialize(createNamespaceMsg.build());
146  }
147
148  @Override
149  protected void deserializeStateData(ProcedureStateSerializer serializer) throws IOException {
150    super.deserializeStateData(serializer);
151
152    MasterProcedureProtos.CreateNamespaceStateData createNamespaceMsg =
153      serializer.deserialize(MasterProcedureProtos.CreateNamespaceStateData.class);
154    nsDescriptor = ProtobufUtil.toNamespaceDescriptor(createNamespaceMsg.getNamespaceDescriptor());
155  }
156
157  @Override
158  protected boolean waitInitialized(MasterProcedureEnv env) {
159    return env.waitInitialized(this);
160  }
161
162  @Override
163  protected LockState acquireLock(final MasterProcedureEnv env) {
164    if (env.getProcedureScheduler().waitNamespaceExclusiveLock(this, getNamespaceName())) {
165      return LockState.LOCK_EVENT_WAIT;
166    }
167    return LockState.LOCK_ACQUIRED;
168  }
169
170  @Override
171  public TableOperationType getTableOperationType() {
172    return TableOperationType.EDIT;
173  }
174
175  @Override
176  protected String getNamespaceName() {
177    return nsDescriptor.getName();
178  }
179
180  /**
181   * Action before any real action of creating namespace.
182   * @param env MasterProcedureEnv
183   */
184  private boolean prepareCreate(final MasterProcedureEnv env) throws IOException {
185    if (getTableNamespaceManager(env).doesNamespaceExist(nsDescriptor.getName())) {
186      setFailure("master-create-namespace",
187        new NamespaceExistException("Namespace " + nsDescriptor.getName() + " already exists"));
188      return false;
189    }
190    getTableNamespaceManager(env).validateTableAndRegionCount(nsDescriptor);
191    checkNamespaceRSGroup(env, nsDescriptor);
192    return true;
193  }
194
195  /**
196   * Set quota for the namespace
197   * @param env          MasterProcedureEnv
198   * @param nsDescriptor NamespaceDescriptor
199   **/
200  private static void setNamespaceQuota(final MasterProcedureEnv env,
201    final NamespaceDescriptor nsDescriptor) throws IOException {
202    if (env.getMasterServices().isInitialized()) {
203      env.getMasterServices().getMasterQuotaManager().setNamespaceQuota(nsDescriptor);
204    }
205  }
206}