View Javadoc

1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  
19  package org.apache.hadoop.hbase.master.procedure;
20  
21  import java.io.IOException;
22  import java.io.InputStream;
23  import java.io.OutputStream;
24  import java.util.concurrent.atomic.AtomicBoolean;
25  
26  import org.apache.commons.logging.Log;
27  import org.apache.commons.logging.LogFactory;
28  import org.apache.hadoop.hbase.NamespaceDescriptor;
29  import org.apache.hadoop.hbase.NamespaceExistException;
30  import org.apache.hadoop.hbase.TableName;
31  import org.apache.hadoop.hbase.classification.InterfaceAudience;
32  import org.apache.hadoop.hbase.master.MasterFileSystem;
33  import org.apache.hadoop.hbase.master.TableNamespaceManager;
34  import org.apache.hadoop.hbase.procedure2.StateMachineProcedure;
35  import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
36  import org.apache.hadoop.hbase.protobuf.generated.MasterProcedureProtos;
37  import org.apache.hadoop.hbase.protobuf.generated.MasterProcedureProtos.CreateNamespaceState;
38  import org.apache.hadoop.hbase.util.FSUtils;
39  
40  /**
41   * The procedure to create a new namespace.
42   */
43  @InterfaceAudience.Private
44  public class CreateNamespaceProcedure
45      extends StateMachineProcedure<MasterProcedureEnv, CreateNamespaceState>
46      implements TableProcedureInterface {
47    private static final Log LOG = LogFactory.getLog(CreateNamespaceProcedure.class);
48  
49    private final AtomicBoolean aborted = new AtomicBoolean(false);
50  
51    private NamespaceDescriptor nsDescriptor;
52    private Boolean traceEnabled;
53  
54    public CreateNamespaceProcedure() {
55      this.traceEnabled = null;
56    }
57  
58    public CreateNamespaceProcedure(final MasterProcedureEnv env,
59        final NamespaceDescriptor nsDescriptor) {
60      this.nsDescriptor = nsDescriptor;
61      this.traceEnabled = null;
62      this.setOwner(env.getRequestUser().getUGI().getShortUserName());
63    }
64  
65    @Override
66    protected Flow executeFromState(final MasterProcedureEnv env, final CreateNamespaceState state)
67        throws InterruptedException {
68      if (isTraceEnabled()) {
69        LOG.trace(this + " execute state=" + state);
70      }
71  
72      try {
73        switch (state) {
74        case CREATE_NAMESPACE_PREPARE:
75          prepareCreate(env);
76          setNextState(CreateNamespaceState.CREATE_NAMESPACE_CREATE_DIRECTORY);
77          break;
78        case CREATE_NAMESPACE_CREATE_DIRECTORY:
79          createDirectory(env, nsDescriptor);
80          setNextState(CreateNamespaceState.CREATE_NAMESPACE_INSERT_INTO_NS_TABLE);
81          break;
82        case CREATE_NAMESPACE_INSERT_INTO_NS_TABLE:
83          insertIntoNSTable(env, nsDescriptor);
84          setNextState(CreateNamespaceState.CREATE_NAMESPACE_UPDATE_ZK);
85          break;
86        case CREATE_NAMESPACE_UPDATE_ZK:
87          updateZKNamespaceManager(env, nsDescriptor);
88          setNextState(CreateNamespaceState.CREATE_NAMESPACE_SET_NAMESPACE_QUOTA);
89          break;
90        case CREATE_NAMESPACE_SET_NAMESPACE_QUOTA:
91          setNamespaceQuota(env, nsDescriptor);
92          return Flow.NO_MORE_STATE;
93        default:
94          throw new UnsupportedOperationException(this + " unhandled state=" + state);
95        }
96      } catch (IOException e) {
97        LOG.warn("Error trying to create the namespace" + nsDescriptor.getName()
98          + " (in state=" + state + ")", e);
99  
100       setFailure("master-create-namespace", e);
101     }
102     return Flow.HAS_MORE_STATE;
103   }
104 
105   @Override
106   protected void rollbackState(final MasterProcedureEnv env, final CreateNamespaceState state)
107       throws IOException {
108     if (isTraceEnabled()) {
109       LOG.trace(this + " rollback state=" + state);
110     }
111     try {
112       switch (state) {
113       case CREATE_NAMESPACE_SET_NAMESPACE_QUOTA:
114         rollbackSetNamespaceQuota(env);
115         break;
116       case CREATE_NAMESPACE_UPDATE_ZK:
117         rollbackZKNamespaceManagerChange(env);
118         break;
119       case CREATE_NAMESPACE_INSERT_INTO_NS_TABLE:
120         rollbackInsertIntoNSTable(env);
121         break;
122       case CREATE_NAMESPACE_CREATE_DIRECTORY:
123         rollbackCreateDirectory(env);
124         break;
125       case CREATE_NAMESPACE_PREPARE:
126         break; // nothing to do
127       default:
128         throw new UnsupportedOperationException(this + " unhandled state=" + state);
129       }
130     } catch (IOException e) {
131       // This will be retried. Unless there is a bug in the code,
132       // this should be just a "temporary error" (e.g. network down)
133       LOG.warn("Failed rollback attempt step " + state + " for creating the namespace "
134           + nsDescriptor.getName(), e);
135       throw e;
136     }
137   }
138 
139   @Override
140   protected CreateNamespaceState getState(final int stateId) {
141     return CreateNamespaceState.valueOf(stateId);
142   }
143 
144   @Override
145   protected int getStateId(final CreateNamespaceState state) {
146     return state.getNumber();
147   }
148 
149   @Override
150   protected CreateNamespaceState getInitialState() {
151     return CreateNamespaceState.CREATE_NAMESPACE_PREPARE;
152   }
153 
154   @Override
155   protected void setNextState(CreateNamespaceState state) {
156     if (aborted.get()) {
157       setAbortFailure("create-namespace", "abort requested");
158     } else {
159       super.setNextState(state);
160     }
161   }
162 
163   @Override
164   public boolean abort(final MasterProcedureEnv env) {
165     aborted.set(true);
166     return true;
167   }
168 
169   @Override
170   public void serializeStateData(final OutputStream stream) throws IOException {
171     super.serializeStateData(stream);
172 
173     MasterProcedureProtos.CreateNamespaceStateData.Builder createNamespaceMsg =
174         MasterProcedureProtos.CreateNamespaceStateData.newBuilder().setNamespaceDescriptor(
175           ProtobufUtil.toProtoNamespaceDescriptor(this.nsDescriptor));
176     createNamespaceMsg.build().writeDelimitedTo(stream);
177   }
178 
179   @Override
180   public void deserializeStateData(final InputStream stream) throws IOException {
181     super.deserializeStateData(stream);
182 
183     MasterProcedureProtos.CreateNamespaceStateData createNamespaceMsg =
184         MasterProcedureProtos.CreateNamespaceStateData.parseDelimitedFrom(stream);
185     nsDescriptor = ProtobufUtil.toNamespaceDescriptor(createNamespaceMsg.getNamespaceDescriptor());
186   }
187 
188   @Override
189   public void toStringClassDetails(StringBuilder sb) {
190     sb.append(getClass().getSimpleName());
191     sb.append(" (Namespace=");
192     sb.append(nsDescriptor.getName());
193     sb.append(")");
194   }
195 
196   private boolean isBootstrapNamespace() {
197     return nsDescriptor.equals(NamespaceDescriptor.DEFAULT_NAMESPACE) ||
198         nsDescriptor.equals(NamespaceDescriptor.SYSTEM_NAMESPACE);
199   }
200 
201   @Override
202   protected boolean acquireLock(final MasterProcedureEnv env) {
203     if (!env.getMasterServices().isInitialized()) {
204       // Namespace manager might not be ready if master is not fully initialized,
205       // return false to reject user namespace creation; return true for default
206       // and system namespace creation (this is part of master initialization).
207       if (!isBootstrapNamespace() && env.waitInitialized(this)) {
208         return false;
209       }
210     }
211     return env.getProcedureQueue().tryAcquireNamespaceExclusiveLock(this, getNamespaceName());
212   }
213 
214   @Override
215   protected void releaseLock(final MasterProcedureEnv env) {
216     env.getProcedureQueue().releaseNamespaceExclusiveLock(this, getNamespaceName());
217   }
218 
219   @Override
220   public TableName getTableName() {
221     return TableName.NAMESPACE_TABLE_NAME;
222   }
223 
224   @Override
225   public TableOperationType getTableOperationType() {
226     return TableOperationType.EDIT;
227   }
228 
229   private String getNamespaceName() {
230     return nsDescriptor.getName();
231   }
232 
233   /**
234    * Action before any real action of creating namespace.
235    * @param env MasterProcedureEnv
236    * @throws IOException
237    */
238   private void prepareCreate(final MasterProcedureEnv env) throws IOException {
239     if (getTableNamespaceManager(env).doesNamespaceExist(nsDescriptor.getName())) {
240       throw new NamespaceExistException(nsDescriptor.getName());
241     }
242     getTableNamespaceManager(env).validateTableAndRegionCount(nsDescriptor);
243   }
244 
245   /**
246    * Create the namespace directory
247    * @param env MasterProcedureEnv
248    * @param nsDescriptor NamespaceDescriptor
249    * @throws IOException
250    */
251   protected static void createDirectory(
252       final MasterProcedureEnv env,
253       final NamespaceDescriptor nsDescriptor) throws IOException {
254     MasterFileSystem mfs = env.getMasterServices().getMasterFileSystem();
255     mfs.getFileSystem().mkdirs(
256       FSUtils.getNamespaceDir(mfs.getRootDir(), nsDescriptor.getName()));
257   }
258 
259   /**
260    * undo create directory
261    * @param env MasterProcedureEnv
262    * @throws IOException
263    */
264   private void rollbackCreateDirectory(final MasterProcedureEnv env) throws IOException {
265     try {
266       DeleteNamespaceProcedure.deleteDirectory(env, nsDescriptor.getName());
267     } catch (Exception e) {
268       // Ignore exception
269       LOG.debug("Rollback of createDirectory throws exception: " + e);
270     }
271   }
272 
273   /**
274    * Insert the row into ns table
275    * @param env MasterProcedureEnv
276    * @param nsDescriptor NamespaceDescriptor
277    * @throws IOException
278    */
279   protected static void insertIntoNSTable(
280       final MasterProcedureEnv env,
281       final NamespaceDescriptor nsDescriptor) throws IOException {
282     getTableNamespaceManager(env).insertIntoNSTable(nsDescriptor);
283   }
284 
285   /**
286    * Undo the insert.
287    * @param env MasterProcedureEnv
288    * @throws IOException
289    */
290   private void rollbackInsertIntoNSTable(final MasterProcedureEnv env) throws IOException {
291     try {
292       DeleteNamespaceProcedure.deleteFromNSTable(env, nsDescriptor.getName());
293     } catch (Exception e) {
294       // Ignore exception
295       LOG.debug("Rollback of insertIntoNSTable throws exception: " + e);
296     }
297   }
298 
299   /**
300    * Update Zookeeper.
301    * @param env MasterProcedureEnv
302    * @param nsDescriptor NamespaceDescriptor
303    * @throws IOException
304    */
305   protected static void updateZKNamespaceManager(
306       final MasterProcedureEnv env,
307       final NamespaceDescriptor nsDescriptor) throws IOException {
308     getTableNamespaceManager(env).updateZKNamespaceManager(nsDescriptor);
309   }
310 
311   /**
312    * rollback Zookeeper update.
313    * @param env MasterProcedureEnv
314    * @throws IOException
315    */
316   private void rollbackZKNamespaceManagerChange(final MasterProcedureEnv env) throws IOException {
317     try {
318       DeleteNamespaceProcedure.removeFromZKNamespaceManager(env, nsDescriptor.getName());
319     } catch (Exception e) {
320       // Ignore exception
321       LOG.debug("Rollback of updateZKNamespaceManager throws exception: " + e);
322     }
323   }
324 
325   /**
326    * Set quota for the namespace
327    * @param env MasterProcedureEnv
328    * @param nsDescriptor NamespaceDescriptor
329    * @throws IOException
330    **/
331   protected static void setNamespaceQuota(
332       final MasterProcedureEnv env,
333       final NamespaceDescriptor nsDescriptor) throws IOException {
334     if (env.getMasterServices().isInitialized()) {
335       env.getMasterServices().getMasterQuotaManager().setNamespaceQuota(nsDescriptor);
336     }
337   }
338 
339   /**
340    * remove quota for the namespace if exists
341    * @param env MasterProcedureEnv
342    * @throws IOException
343    **/
344   private void rollbackSetNamespaceQuota(final MasterProcedureEnv env) throws IOException {
345     try {
346       DeleteNamespaceProcedure.removeNamespaceQuota(env, nsDescriptor.getName());
347     } catch (Exception e) {
348       // Ignore exception
349       LOG.debug("Rollback of setNamespaceQuota throws exception: " + e);
350     }
351   }
352 
353   private static TableNamespaceManager getTableNamespaceManager(final MasterProcedureEnv env) {
354     return env.getMasterServices().getTableNamespaceManager();
355   }
356 
357   /**
358    * The procedure could be restarted from a different machine. If the variable is null, we need to
359    * retrieve it.
360    * @return traceEnabled
361    */
362   private Boolean isTraceEnabled() {
363     if (traceEnabled == null) {
364       traceEnabled = LOG.isTraceEnabled();
365     }
366     return traceEnabled;
367   }
368 
369   @Override
370   protected boolean shouldWaitClientAck(MasterProcedureEnv env) {
371     // hbase and default namespaces are created on bootstrap internally by the system
372     // the client does not know about this procedures.
373     return !isBootstrapNamespace();
374   }
375 }