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 java.util.Collections; 022import java.util.List; 023import java.util.stream.Collectors; 024import org.apache.hadoop.hbase.HBaseIOException; 025import org.apache.hadoop.hbase.HRegionLocation; 026import org.apache.hadoop.hbase.TableName; 027import org.apache.hadoop.hbase.master.RegionPlan; 028import org.apache.hadoop.hbase.master.assignment.MoveRegionProcedure; 029import org.apache.hadoop.hbase.procedure2.ProcedureStateSerializer; 030import org.apache.hadoop.hbase.procedure2.ProcedureSuspendedException; 031import org.apache.hadoop.hbase.procedure2.ProcedureUtil; 032import org.apache.hadoop.hbase.procedure2.ProcedureYieldException; 033import org.apache.yetus.audience.InterfaceAudience; 034import org.slf4j.Logger; 035import org.slf4j.LoggerFactory; 036 037import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil; 038import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProcedureProtos.ReopenTableRegionsState; 039import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProcedureProtos.ReopenTableRegionsStateData; 040import org.apache.hadoop.hbase.shaded.protobuf.generated.ProcedureProtos; 041 042/** 043 * Used for reopening the regions for a table. 044 * <p/> 045 * Currently we use {@link MoveRegionProcedure} to reopen regions. 046 */ 047@InterfaceAudience.Private 048public class ReopenTableRegionsProcedure 049 extends AbstractStateMachineTableProcedure<ReopenTableRegionsState> { 050 051 private static final Logger LOG = LoggerFactory.getLogger(ReopenTableRegionsProcedure.class); 052 053 private TableName tableName; 054 055 private List<HRegionLocation> regions = Collections.emptyList(); 056 057 private int attempt; 058 059 public ReopenTableRegionsProcedure() { 060 } 061 062 public ReopenTableRegionsProcedure(TableName tableName) { 063 this.tableName = tableName; 064 } 065 066 @Override 067 public TableName getTableName() { 068 return tableName; 069 } 070 071 @Override 072 public TableOperationType getTableOperationType() { 073 return TableOperationType.REGION_EDIT; 074 } 075 076 private MoveRegionProcedure createReopenProcedure(MasterProcedureEnv env, HRegionLocation loc) { 077 try { 078 return new MoveRegionProcedure(env, 079 new RegionPlan(loc.getRegion(), loc.getServerName(), loc.getServerName()), false); 080 } catch (HBaseIOException e) { 081 // we skip the checks so this should not happen 082 throw new AssertionError(e); 083 } 084 } 085 086 @Override 087 protected Flow executeFromState(MasterProcedureEnv env, ReopenTableRegionsState state) 088 throws ProcedureSuspendedException, ProcedureYieldException, InterruptedException { 089 switch (state) { 090 case REOPEN_TABLE_REGIONS_GET_REGIONS: 091 if (!env.getAssignmentManager().isTableEnabled(tableName)) { 092 LOG.info("Table {} is disabled, give up reopening its regions"); 093 return Flow.NO_MORE_STATE; 094 } 095 regions = 096 env.getAssignmentManager().getRegionStates().getRegionsOfTableForReopen(tableName); 097 setNextState(ReopenTableRegionsState.REOPEN_TABLE_REGIONS_REOPEN_REGIONS); 098 return Flow.HAS_MORE_STATE; 099 case REOPEN_TABLE_REGIONS_REOPEN_REGIONS: 100 addChildProcedure(regions.stream().filter(l -> l.getSeqNum() >= 0) 101 .map(l -> createReopenProcedure(env, l)).toArray(MoveRegionProcedure[]::new)); 102 setNextState(ReopenTableRegionsState.REOPEN_TABLE_REGIONS_CONFIRM_REOPENED); 103 return Flow.HAS_MORE_STATE; 104 case REOPEN_TABLE_REGIONS_CONFIRM_REOPENED: 105 regions = regions.stream().map(env.getAssignmentManager().getRegionStates()::checkReopened) 106 .filter(l -> l != null).collect(Collectors.toList()); 107 if (regions.isEmpty()) { 108 return Flow.NO_MORE_STATE; 109 } 110 if (regions.stream().anyMatch(l -> l.getSeqNum() >= 0)) { 111 attempt = 0; 112 setNextState(ReopenTableRegionsState.REOPEN_TABLE_REGIONS_REOPEN_REGIONS); 113 return Flow.HAS_MORE_STATE; 114 } 115 // All the regions need to reopen are in OPENING state which means we can not schedule any 116 // MRPs. 117 long backoff = ProcedureUtil.getBackoffTimeMs(this.attempt++); 118 LOG.info( 119 "There are still {} region(s) which need to be reopened for table {} are in " + 120 "OPENING state, suspend {}secs and try again later", 121 regions.size(), tableName, backoff / 1000); 122 setTimeout(Math.toIntExact(backoff)); 123 setState(ProcedureProtos.ProcedureState.WAITING_TIMEOUT); 124 throw new ProcedureSuspendedException(); 125 default: 126 throw new UnsupportedOperationException("unhandled state=" + state); 127 } 128 } 129 130 /** 131 * At end of timeout, wake ourselves up so we run again. 132 */ 133 @Override 134 protected synchronized boolean setTimeoutFailure(MasterProcedureEnv env) { 135 setState(ProcedureProtos.ProcedureState.RUNNABLE); 136 env.getProcedureScheduler().addFront(this); 137 return false; // 'false' means that this procedure handled the timeout 138 } 139 @Override 140 protected void rollbackState(MasterProcedureEnv env, ReopenTableRegionsState state) 141 throws IOException, InterruptedException { 142 throw new UnsupportedOperationException("unhandled state=" + state); 143 } 144 145 @Override 146 protected ReopenTableRegionsState getState(int stateId) { 147 return ReopenTableRegionsState.forNumber(stateId); 148 } 149 150 @Override 151 protected int getStateId(ReopenTableRegionsState state) { 152 return state.getNumber(); 153 } 154 155 @Override 156 protected ReopenTableRegionsState getInitialState() { 157 return ReopenTableRegionsState.REOPEN_TABLE_REGIONS_GET_REGIONS; 158 } 159 160 @Override 161 protected void serializeStateData(ProcedureStateSerializer serializer) throws IOException { 162 super.serializeStateData(serializer); 163 ReopenTableRegionsStateData.Builder builder = ReopenTableRegionsStateData.newBuilder() 164 .setTableName(ProtobufUtil.toProtoTableName(tableName)); 165 regions.stream().map(ProtobufUtil::toRegionLocation).forEachOrdered(builder::addRegion); 166 serializer.serialize(builder.build()); 167 } 168 169 @Override 170 protected void deserializeStateData(ProcedureStateSerializer serializer) throws IOException { 171 super.deserializeStateData(serializer); 172 ReopenTableRegionsStateData data = serializer.deserialize(ReopenTableRegionsStateData.class); 173 tableName = ProtobufUtil.toTableName(data.getTableName()); 174 regions = data.getRegionList().stream().map(ProtobufUtil::toRegionLocation) 175 .collect(Collectors.toList()); 176 } 177}