001/*
002 *
003 * Licensed to the Apache Software Foundation (ASF) under one
004 * or more contributor license agreements.  See the NOTICE file
005 * distributed with this work for additional information
006 * regarding copyright ownership.  The ASF licenses this file
007 * to you under the Apache License, Version 2.0 (the
008 * "License"); you may not use this file except in compliance
009 * with the License.  You may obtain a copy of the License at
010 *
011 *     http://www.apache.org/licenses/LICENSE-2.0
012 *
013 * Unless required by applicable law or agreed to in writing, software
014 * distributed under the License is distributed on an "AS IS" BASIS,
015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
016 * See the License for the specific language governing permissions and
017 * limitations under the License.
018 */
019
020package org.apache.hadoop.hbase.master.assignment;
021
022import java.io.IOException;
023
024import org.apache.hadoop.hbase.HBaseIOException;
025import org.apache.hadoop.hbase.ServerName;
026import org.apache.hadoop.hbase.TableName;
027import org.apache.hadoop.hbase.client.DoNotRetryRegionException;
028import org.apache.hadoop.hbase.client.RegionInfo;
029import org.apache.hadoop.hbase.master.RegionPlan;
030import org.apache.hadoop.hbase.master.procedure.AbstractStateMachineRegionProcedure;
031import org.apache.hadoop.hbase.master.procedure.MasterProcedureEnv;
032import org.apache.hadoop.hbase.procedure2.ProcedureStateSerializer;
033import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil;
034import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProcedureProtos.MoveRegionState;
035import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProcedureProtos.MoveRegionStateData;
036import org.apache.hbase.thirdparty.com.google.common.annotations.VisibleForTesting;
037import org.apache.yetus.audience.InterfaceAudience;
038import org.slf4j.Logger;
039import org.slf4j.LoggerFactory;
040
041/**
042 * Procedure that implements a RegionPlan.
043 * It first runs an unassign subprocedure followed
044 * by an assign subprocedure. It takes a lock on the region being moved.
045 * It holds the lock for the life of the procedure.
046 *
047 * <p>Throws exception on construction if determines context hostile to move (cluster going
048 * down or master is shutting down or table is disabled).</p>
049 */
050@InterfaceAudience.Private
051public class MoveRegionProcedure extends AbstractStateMachineRegionProcedure<MoveRegionState> {
052  private static final Logger LOG = LoggerFactory.getLogger(MoveRegionProcedure.class);
053  private RegionPlan plan;
054
055  public MoveRegionProcedure() {
056    // Required by the Procedure framework to create the procedure on replay
057    super();
058  }
059
060  @VisibleForTesting
061  protected RegionPlan getPlan() {
062    return this.plan;
063  }
064
065  /**
066   * @param check whether we should do some checks in the constructor. We will skip the checks if we
067   *          are reopening a region as this may fail the whole procedure and cause stuck. We will
068   *          do the check later when actually executing the procedure so not a big problem.
069   * @throws IOException If the cluster is offline or master is stopping or if table is disabled or
070   *           non-existent.
071   */
072  public MoveRegionProcedure(MasterProcedureEnv env, RegionPlan plan, boolean check)
073      throws HBaseIOException {
074    super(env, plan.getRegionInfo());
075    this.plan = plan;
076    if (check) {
077      preflightChecks(env, true);
078      checkOnline(env, plan.getRegionInfo());
079    }
080  }
081
082  @Override
083  protected Flow executeFromState(final MasterProcedureEnv env, final MoveRegionState state)
084      throws InterruptedException {
085    LOG.trace("{} execute state={}", this, state);
086    switch (state) {
087      case MOVE_REGION_PREPARE:
088        // Check context again and that region is online; do it here after we have lock on region.
089        try {
090          preflightChecks(env, true);
091          checkOnline(env, this.plan.getRegionInfo());
092          if (!env.getMasterServices().getServerManager().isServerOnline(this.plan.getSource())) {
093            throw new HBaseIOException(this.plan.getSource() + " not online");
094          }
095        } catch (HBaseIOException e) {
096          LOG.warn(this.toString() + " FAILED because " + e.toString());
097          return Flow.NO_MORE_STATE;
098        }
099        break;
100      case MOVE_REGION_UNASSIGN:
101        try {
102          checkOnline(env, this.plan.getRegionInfo());
103        } catch (DoNotRetryRegionException dnrre) {
104          LOG.info("Skipping move, {} is not online; {}", getRegion().getEncodedName(), this,
105              dnrre);
106          return Flow.NO_MORE_STATE;
107        }
108        addChildProcedure(new UnassignProcedure(plan.getRegionInfo(), plan.getSource(),
109            plan.getDestination(), true));
110        setNextState(MoveRegionState.MOVE_REGION_ASSIGN);
111        break;
112      case MOVE_REGION_ASSIGN:
113        AssignProcedure assignProcedure = plan.getDestination() == null ?
114            new AssignProcedure(plan.getRegionInfo()):
115            new AssignProcedure(plan.getRegionInfo(), plan.getDestination());
116        addChildProcedure(assignProcedure);
117        return Flow.NO_MORE_STATE;
118      default:
119        throw new UnsupportedOperationException("unhandled state=" + state);
120    }
121    return Flow.HAS_MORE_STATE;
122  }
123
124  @Override
125  protected void rollbackState(final MasterProcedureEnv env, final MoveRegionState state)
126      throws IOException {
127    // no-op
128  }
129
130  @Override
131  public boolean abort(final MasterProcedureEnv env) {
132    return false;
133  }
134
135  @Override
136  public void toStringClassDetails(final StringBuilder sb) {
137    sb.append(getClass().getSimpleName());
138    sb.append(" ");
139    sb.append(plan);
140  }
141
142  @Override
143  protected MoveRegionState getInitialState() {
144    return MoveRegionState.MOVE_REGION_UNASSIGN;
145  }
146
147  @Override
148  protected int getStateId(final MoveRegionState state) {
149    return state.getNumber();
150  }
151
152  @Override
153  protected MoveRegionState getState(final int stateId) {
154    return MoveRegionState.forNumber(stateId);
155  }
156
157  @Override
158  public TableName getTableName() {
159    return plan.getRegionInfo().getTable();
160  }
161
162  @Override
163  public TableOperationType getTableOperationType() {
164    return TableOperationType.REGION_EDIT;
165  }
166
167  @Override
168  protected void serializeStateData(ProcedureStateSerializer serializer)
169      throws IOException {
170    super.serializeStateData(serializer);
171
172    final MoveRegionStateData.Builder state = MoveRegionStateData.newBuilder()
173        // No need to serialize the RegionInfo. The super class has the region.
174        .setSourceServer(ProtobufUtil.toServerName(plan.getSource()));
175    if (plan.getDestination() != null) {
176      state.setDestinationServer(ProtobufUtil.toServerName(plan.getDestination()));
177    }
178
179    serializer.serialize(state.build());
180  }
181
182  @Override
183  protected void deserializeStateData(ProcedureStateSerializer serializer)
184      throws IOException {
185    super.deserializeStateData(serializer);
186
187    final MoveRegionStateData state = serializer.deserialize(MoveRegionStateData.class);
188    final RegionInfo regionInfo = getRegion(); // Get it from super class deserialization.
189    final ServerName sourceServer = ProtobufUtil.toServerName(state.getSourceServer());
190    final ServerName destinationServer = state.hasDestinationServer() ?
191        ProtobufUtil.toServerName(state.getDestinationServer()) : null;
192    this.plan = new RegionPlan(regionInfo, sourceServer, destinationServer);
193  }
194
195  @Override
196  protected boolean waitInitialized(MasterProcedureEnv env) {
197
198    if (TableName.isMetaTableName(getTableName())) {
199      // only offline state master will try init meta procedure
200      return false;
201    }
202
203    if (getTableName().equals(TableName.NAMESPACE_TABLE_NAME)) {
204      //  after unassign procedure finished, namespace region will be offline
205      //  if master crashed at the same time and reboot
206      //  it will be stuck as master init is block by  waiting namespace table online
207      //  but move region procedure can not go on, break the deadlock by not wait master initialized
208      return false;
209    }
210    return super.waitInitialized(env);
211  }
212}