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}