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}