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