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.NamespaceNotFoundException;
025import org.apache.hadoop.hbase.constraint.ConstraintException;
026import org.apache.yetus.audience.InterfaceAudience;
027import org.slf4j.Logger;
028import org.slf4j.LoggerFactory;
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.ModifyNamespaceState;
034
035/**
036 * The procedure to add a namespace to an existing table.
037 */
038@InterfaceAudience.Private
039public class ModifyNamespaceProcedure
040    extends AbstractStateMachineNamespaceProcedure<ModifyNamespaceState> {
041  private static final Logger LOG = LoggerFactory.getLogger(ModifyNamespaceProcedure.class);
042
043  private NamespaceDescriptor oldNsDescriptor;
044  private NamespaceDescriptor newNsDescriptor;
045  private Boolean traceEnabled;
046
047  public ModifyNamespaceProcedure() {
048    this.oldNsDescriptor = null;
049    this.traceEnabled = null;
050  }
051
052  public ModifyNamespaceProcedure(final MasterProcedureEnv env,
053      final NamespaceDescriptor newNsDescriptor) {
054    this(env, newNsDescriptor, null);
055  }
056
057  public ModifyNamespaceProcedure(final MasterProcedureEnv env,
058      final NamespaceDescriptor newNsDescriptor, final ProcedurePrepareLatch latch) {
059    super(env, latch);
060    this.oldNsDescriptor = null;
061    this.newNsDescriptor = newNsDescriptor;
062    this.traceEnabled = null;
063  }
064
065  @Override
066  protected Flow executeFromState(final MasterProcedureEnv env, final ModifyNamespaceState state)
067      throws InterruptedException {
068    if (isTraceEnabled()) {
069      LOG.trace(this + " execute state=" + state);
070    }
071
072    try {
073      switch (state) {
074      case MODIFY_NAMESPACE_PREPARE:
075        boolean success = prepareModify(env);
076        releaseSyncLatch();
077        if (!success) {
078          assert isFailed() : "Modify namespace should have an exception here";
079          return Flow.NO_MORE_STATE;
080        }
081        setNextState(ModifyNamespaceState.MODIFY_NAMESPACE_UPDATE_NS_TABLE);
082        break;
083      case MODIFY_NAMESPACE_UPDATE_NS_TABLE:
084        insertIntoNSTable(env);
085        setNextState(ModifyNamespaceState.MODIFY_NAMESPACE_UPDATE_ZK);
086        break;
087      case MODIFY_NAMESPACE_UPDATE_ZK:
088        updateZKNamespaceManager(env);
089        return Flow.NO_MORE_STATE;
090      default:
091        throw new UnsupportedOperationException(this + " unhandled state=" + state);
092      }
093    } catch (IOException e) {
094      if (isRollbackSupported(state)) {
095        setFailure("master-modify-namespace", e);
096      } else {
097        LOG.warn("Retriable error trying to modify namespace=" + newNsDescriptor.getName() +
098          " (in state=" + state + ")", e);
099      }
100    }
101    return Flow.HAS_MORE_STATE;
102  }
103
104  @Override
105  protected void rollbackState(final MasterProcedureEnv env, final ModifyNamespaceState state)
106      throws IOException {
107    if (state == ModifyNamespaceState.MODIFY_NAMESPACE_PREPARE) {
108      // nothing to rollback, pre-modify is just checks.
109      // TODO: coprocessor rollback semantic is still undefined.
110      releaseSyncLatch();
111      return;
112    }
113
114    // The procedure doesn't have a rollback. The execution will succeed, at some point.
115    throw new UnsupportedOperationException("unhandled state=" + state);
116  }
117
118  @Override
119  protected boolean isRollbackSupported(final ModifyNamespaceState state) {
120    switch (state) {
121      case MODIFY_NAMESPACE_PREPARE:
122        return true;
123      default:
124        return false;
125    }
126  }
127
128  @Override
129  protected ModifyNamespaceState getState(final int stateId) {
130    return ModifyNamespaceState.valueOf(stateId);
131  }
132
133  @Override
134  protected int getStateId(final ModifyNamespaceState state) {
135    return state.getNumber();
136  }
137
138  @Override
139  protected ModifyNamespaceState getInitialState() {
140    return ModifyNamespaceState.MODIFY_NAMESPACE_PREPARE;
141  }
142
143  @Override
144  protected void serializeStateData(ProcedureStateSerializer serializer)
145      throws IOException {
146    super.serializeStateData(serializer);
147
148    MasterProcedureProtos.ModifyNamespaceStateData.Builder modifyNamespaceMsg =
149        MasterProcedureProtos.ModifyNamespaceStateData.newBuilder().setNamespaceDescriptor(
150          ProtobufUtil.toProtoNamespaceDescriptor(this.newNsDescriptor));
151    if (this.oldNsDescriptor != null) {
152      modifyNamespaceMsg.setUnmodifiedNamespaceDescriptor(
153        ProtobufUtil.toProtoNamespaceDescriptor(this.oldNsDescriptor));
154    }
155    serializer.serialize(modifyNamespaceMsg.build());
156  }
157
158  @Override
159  protected void deserializeStateData(ProcedureStateSerializer serializer)
160      throws IOException {
161    super.deserializeStateData(serializer);
162
163    MasterProcedureProtos.ModifyNamespaceStateData modifyNamespaceMsg =
164        serializer.deserialize(MasterProcedureProtos.ModifyNamespaceStateData.class);
165    newNsDescriptor =
166        ProtobufUtil.toNamespaceDescriptor(modifyNamespaceMsg.getNamespaceDescriptor());
167    if (modifyNamespaceMsg.hasUnmodifiedNamespaceDescriptor()) {
168      oldNsDescriptor =
169          ProtobufUtil.toNamespaceDescriptor(modifyNamespaceMsg.getUnmodifiedNamespaceDescriptor());
170    }
171  }
172
173  @Override
174  public TableOperationType getTableOperationType() {
175    return TableOperationType.EDIT;
176  }
177
178  @Override
179  protected String getNamespaceName() {
180    return newNsDescriptor.getName();
181  }
182
183  /**
184   * Action before any real action of adding namespace.
185   * @param env MasterProcedureEnv
186   * @throws IOException
187   */
188  private boolean prepareModify(final MasterProcedureEnv env) throws IOException {
189    if (getTableNamespaceManager(env).doesNamespaceExist(newNsDescriptor.getName()) == false) {
190      setFailure("master-modify-namespace", new NamespaceNotFoundException(
191            newNsDescriptor.getName()));
192      return false;
193    }
194    try {
195      getTableNamespaceManager(env).validateTableAndRegionCount(newNsDescriptor);
196    } catch (ConstraintException e) {
197      setFailure("master-modify-namespace", e);
198      return false;
199    }
200
201    // This is used for rollback
202    oldNsDescriptor = getTableNamespaceManager(env).get(newNsDescriptor.getName());
203    return true;
204  }
205
206  /**
207   * Insert/update the row into namespace table
208   * @param env MasterProcedureEnv
209   * @throws IOException
210   */
211  private void insertIntoNSTable(final MasterProcedureEnv env) throws IOException {
212    getTableNamespaceManager(env).insertIntoNSTable(newNsDescriptor);
213  }
214
215  /**
216   * Update ZooKeeper.
217   * @param env MasterProcedureEnv
218   * @throws IOException
219   */
220  private void updateZKNamespaceManager(final MasterProcedureEnv env) throws IOException {
221    getTableNamespaceManager(env).updateZKNamespaceManager(newNsDescriptor);
222  }
223
224  private TableNamespaceManager getTableNamespaceManager(final MasterProcedureEnv env) {
225    return env.getMasterServices().getClusterSchema().getTableNamespaceManager();
226  }
227
228  /**
229   * The procedure could be restarted from a different machine. If the variable is null, we need to
230   * retrieve it.
231   * @return traceEnabled
232   */
233  private Boolean isTraceEnabled() {
234    if (traceEnabled == null) {
235      traceEnabled = LOG.isTraceEnabled();
236    }
237    return traceEnabled;
238  }
239}