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.NamespaceNotFoundException;
30  import org.apache.hadoop.hbase.TableName;
31  import org.apache.hadoop.hbase.classification.InterfaceAudience;
32  import org.apache.hadoop.hbase.master.TableNamespaceManager;
33  import org.apache.hadoop.hbase.procedure2.StateMachineProcedure;
34  import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
35  import org.apache.hadoop.hbase.protobuf.generated.MasterProcedureProtos;
36  import org.apache.hadoop.hbase.protobuf.generated.MasterProcedureProtos.ModifyNamespaceState;
37  
38  /**
39   * The procedure to add a namespace to an existing table.
40   */
41  @InterfaceAudience.Private
42  public class ModifyNamespaceProcedure
43      extends StateMachineProcedure<MasterProcedureEnv, ModifyNamespaceState>
44      implements TableProcedureInterface {
45    private static final Log LOG = LogFactory.getLog(ModifyNamespaceProcedure.class);
46  
47    private final AtomicBoolean aborted = new AtomicBoolean(false);
48  
49    private NamespaceDescriptor oldNsDescriptor;
50    private NamespaceDescriptor newNsDescriptor;
51    private Boolean traceEnabled;
52  
53    public ModifyNamespaceProcedure() {
54      this.oldNsDescriptor = null;
55      this.traceEnabled = null;
56    }
57  
58    public ModifyNamespaceProcedure(final MasterProcedureEnv env,
59        final NamespaceDescriptor newNsDescriptor) {
60      this.oldNsDescriptor = null;
61      this.newNsDescriptor = newNsDescriptor;
62      this.traceEnabled = null;
63      this.setOwner(env.getRequestUser().getUGI().getShortUserName());
64    }
65  
66    @Override
67    protected Flow executeFromState(final MasterProcedureEnv env, final ModifyNamespaceState state)
68        throws InterruptedException {
69      if (isTraceEnabled()) {
70        LOG.trace(this + " execute state=" + state);
71      }
72  
73      try {
74        switch (state) {
75        case MODIFY_NAMESPACE_PREPARE:
76          prepareModify(env);
77          setNextState(ModifyNamespaceState.MODIFY_NAMESPACE_UPDATE_NS_TABLE);
78          break;
79        case MODIFY_NAMESPACE_UPDATE_NS_TABLE:
80          insertIntoNSTable(env);
81          setNextState(ModifyNamespaceState.MODIFY_NAMESPACE_UPDATE_ZK);
82          break;
83        case MODIFY_NAMESPACE_UPDATE_ZK:
84          updateZKNamespaceManager(env);
85          return Flow.NO_MORE_STATE;
86        default:
87          throw new UnsupportedOperationException(this + " unhandled state=" + state);
88        }
89      } catch (IOException e) {
90        LOG.warn("Error trying to modify the namespace" + newNsDescriptor.getName()
91          + " (in state=" + state + ")", e);
92  
93        setFailure("master-modify-namespace", e);
94      }
95      return Flow.HAS_MORE_STATE;
96    }
97  
98    @Override
99    protected void rollbackState(final MasterProcedureEnv env, final ModifyNamespaceState state)
100       throws IOException {
101     if (isTraceEnabled()) {
102       LOG.trace(this + " rollback state=" + state);
103     }
104     try {
105       switch (state) {
106       case MODIFY_NAMESPACE_UPDATE_ZK:
107         rollbackZKNamespaceManagerChange(env);
108         break;
109       case MODIFY_NAMESPACE_UPDATE_NS_TABLE:
110         rollbackUpdateInNSTable(env);
111         break;
112       case MODIFY_NAMESPACE_PREPARE:
113         break; // nothing to do
114       default:
115         throw new UnsupportedOperationException(this + " unhandled state=" + state);
116       }
117     } catch (IOException e) {
118       // This will be retried. Unless there is a bug in the code,
119       // this should be just a "temporary error" (e.g. network down)
120       LOG.warn("Failed rollback attempt step " + state + " for creating the namespace "
121           + newNsDescriptor.getName(), e);
122       throw e;
123     }
124   }
125 
126   @Override
127   protected ModifyNamespaceState getState(final int stateId) {
128     return ModifyNamespaceState.valueOf(stateId);
129   }
130 
131   @Override
132   protected int getStateId(final ModifyNamespaceState state) {
133     return state.getNumber();
134   }
135 
136   @Override
137   protected ModifyNamespaceState getInitialState() {
138     return ModifyNamespaceState.MODIFY_NAMESPACE_PREPARE;
139   }
140 
141   @Override
142   protected void setNextState(ModifyNamespaceState state) {
143     if (aborted.get()) {
144       setAbortFailure("modify-namespace", "abort requested");
145     } else {
146       super.setNextState(state);
147     }
148   }
149 
150   @Override
151   public boolean abort(final MasterProcedureEnv env) {
152     aborted.set(true);
153     return true;
154   }
155 
156   @Override
157   public void serializeStateData(final OutputStream stream) throws IOException {
158     super.serializeStateData(stream);
159 
160     MasterProcedureProtos.ModifyNamespaceStateData.Builder modifyNamespaceMsg =
161         MasterProcedureProtos.ModifyNamespaceStateData.newBuilder().setNamespaceDescriptor(
162           ProtobufUtil.toProtoNamespaceDescriptor(this.newNsDescriptor));
163     if (this.oldNsDescriptor != null) {
164       modifyNamespaceMsg.setUnmodifiedNamespaceDescriptor(
165         ProtobufUtil.toProtoNamespaceDescriptor(this.oldNsDescriptor));
166     }
167     modifyNamespaceMsg.build().writeDelimitedTo(stream);
168   }
169 
170   @Override
171   public void deserializeStateData(final InputStream stream) throws IOException {
172     super.deserializeStateData(stream);
173 
174     MasterProcedureProtos.ModifyNamespaceStateData modifyNamespaceMsg =
175         MasterProcedureProtos.ModifyNamespaceStateData.parseDelimitedFrom(stream);
176     newNsDescriptor =
177         ProtobufUtil.toNamespaceDescriptor(modifyNamespaceMsg.getNamespaceDescriptor());
178     if (modifyNamespaceMsg.hasUnmodifiedNamespaceDescriptor()) {
179       oldNsDescriptor =
180           ProtobufUtil.toNamespaceDescriptor(modifyNamespaceMsg.getUnmodifiedNamespaceDescriptor());
181     }
182   }
183 
184   @Override
185   public void toStringClassDetails(StringBuilder sb) {
186     sb.append(getClass().getSimpleName());
187     sb.append(" (Namespace=");
188     sb.append(newNsDescriptor.getName());
189     sb.append(")");
190   }
191 
192   @Override
193   protected boolean acquireLock(final MasterProcedureEnv env) {
194     if (env.waitInitialized(this)) return false;
195     return env.getProcedureQueue().tryAcquireNamespaceExclusiveLock(this, getNamespaceName());
196   }
197 
198   @Override
199   protected void releaseLock(final MasterProcedureEnv env) {
200     env.getProcedureQueue().releaseNamespaceExclusiveLock(this, getNamespaceName());
201   }
202 
203   @Override
204   public TableName getTableName() {
205     return TableName.NAMESPACE_TABLE_NAME;
206   }
207 
208   @Override
209   public TableOperationType getTableOperationType() {
210     return TableOperationType.EDIT;
211   }
212 
213   private String getNamespaceName() {
214     return newNsDescriptor.getName();
215   }
216 
217   /**
218    * Action before any real action of adding namespace.
219    * @param env MasterProcedureEnv
220    * @throws IOException
221    */
222   private void prepareModify(final MasterProcedureEnv env) throws IOException {
223     if (getTableNamespaceManager(env).doesNamespaceExist(newNsDescriptor.getName()) == false) {
224       throw new NamespaceNotFoundException(newNsDescriptor.getName());
225     }
226     getTableNamespaceManager(env).validateTableAndRegionCount(newNsDescriptor);
227 
228     // This is used for rollback
229     oldNsDescriptor = getTableNamespaceManager(env).get(newNsDescriptor.getName());
230   }
231 
232   /**
233    * Insert/update the row into namespace table
234    * @param env MasterProcedureEnv
235    * @throws IOException
236    */
237   private void insertIntoNSTable(final MasterProcedureEnv env) throws IOException {
238     getTableNamespaceManager(env).insertIntoNSTable(newNsDescriptor);
239   }
240 
241   /**
242    * rollback the row into namespace table
243    * @param env MasterProcedureEnv
244    * @throws IOException
245    */
246   private void rollbackUpdateInNSTable(final MasterProcedureEnv env) throws IOException {
247     if (oldNsDescriptor != null) {
248       getTableNamespaceManager(env).insertIntoNSTable(oldNsDescriptor);
249     }
250   }
251 
252   /**
253    * Update Zookeeper.
254    * @param env MasterProcedureEnv
255    * @throws IOException
256    */
257   private void updateZKNamespaceManager(final MasterProcedureEnv env) throws IOException {
258     getTableNamespaceManager(env).updateZKNamespaceManager(newNsDescriptor);
259   }
260 
261   /**
262    * Update Zookeeper during undo.
263    * @param env MasterProcedureEnv
264    * @throws IOException
265    */
266   private void rollbackZKNamespaceManagerChange(final MasterProcedureEnv env) throws IOException {
267     if (oldNsDescriptor != null) {
268       getTableNamespaceManager(env).updateZKNamespaceManager(oldNsDescriptor);
269     }
270   }
271 
272   private TableNamespaceManager getTableNamespaceManager(final MasterProcedureEnv env) {
273     return env.getMasterServices().getTableNamespaceManager();
274   }
275   /**
276    * The procedure could be restarted from a different machine. If the variable is null, we need to
277    * retrieve it.
278    * @return traceEnabled
279    */
280   private Boolean isTraceEnabled() {
281     if (traceEnabled == null) {
282       traceEnabled = LOG.isTraceEnabled();
283     }
284     return traceEnabled;
285   }
286 }