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.function.Consumer; 022import org.apache.commons.lang3.mutable.MutableBoolean; 023import org.apache.hadoop.hbase.TableName; 024import org.apache.hadoop.hbase.master.assignment.TransitRegionStateProcedure; 025import org.apache.hadoop.hbase.procedure2.ProcedureSuspendedException; 026import org.apache.hadoop.hbase.procedure2.ProcedureUtil; 027import org.apache.hadoop.hbase.procedure2.ProcedureYieldException; 028import org.apache.hadoop.hbase.util.RetryCounter; 029import org.apache.yetus.audience.InterfaceAudience; 030import org.slf4j.Logger; 031import org.slf4j.LoggerFactory; 032 033import org.apache.hadoop.hbase.shaded.protobuf.generated.ProcedureProtos; 034 035/** 036 * Base class for unassigning table regions. 037 */ 038@InterfaceAudience.Private 039public abstract class AbstractCloseTableRegionsProcedure<TState extends Enum<?>> 040 extends AbstractStateMachineTableProcedure<TState> { 041 042 private static final Logger LOG = 043 LoggerFactory.getLogger(AbstractCloseTableRegionsProcedure.class); 044 045 protected TableName tableName; 046 047 private RetryCounter retryCounter; 048 049 protected AbstractCloseTableRegionsProcedure() { 050 } 051 052 protected AbstractCloseTableRegionsProcedure(TableName tableName) { 053 this.tableName = tableName; 054 } 055 056 @Override 057 public TableName getTableName() { 058 return tableName; 059 } 060 061 @Override 062 public TableOperationType getTableOperationType() { 063 return TableOperationType.REGION_EDIT; 064 } 065 066 private Flow schedule(MasterProcedureEnv env) throws ProcedureSuspendedException { 067 MutableBoolean submitted = new MutableBoolean(false); 068 int inTransitionCount = submitUnassignProcedure(env, p -> { 069 submitted.setTrue(); 070 addChildProcedure(p); 071 }); 072 if (inTransitionCount > 0 && submitted.isFalse()) { 073 // we haven't scheduled any unassign procedures and there are still regions in 074 // transition, sleep for a while and try again 075 if (retryCounter == null) { 076 retryCounter = ProcedureUtil.createRetryCounter(env.getMasterConfiguration()); 077 } 078 long backoffMillis = retryCounter.getBackoffTimeAndIncrementAttempts(); 079 LOG.info( 080 "There are still {} region(s) in transition for closing regions of table {}" 081 + " when executing {}, suspend {}secs and try again later", 082 inTransitionCount, tableName, getClass().getSimpleName(), backoffMillis / 1000); 083 suspend((int) backoffMillis, true); 084 } 085 setNextState(getConfirmState()); 086 return Flow.HAS_MORE_STATE; 087 } 088 089 private Flow confirm(MasterProcedureEnv env) { 090 int unclosedCount = numberOfUnclosedRegions(env); 091 if (unclosedCount > 0) { 092 LOG.info( 093 "There are still {} unclosed region(s) for closing regions of table {}" 094 + " when executing {}, continue...", 095 unclosedCount, tableName, getClass().getSimpleName()); 096 setNextState(getInitialState()); 097 return Flow.HAS_MORE_STATE; 098 } else { 099 return Flow.NO_MORE_STATE; 100 } 101 } 102 103 @Override 104 protected Flow executeFromState(MasterProcedureEnv env, TState state) 105 throws ProcedureSuspendedException, ProcedureYieldException, InterruptedException { 106 LOG.trace("{} execute state={}", this, state); 107 if (state == getInitialState()) { 108 return schedule(env); 109 } else if (state == getConfirmState()) { 110 return confirm(env); 111 } else { 112 throw new UnsupportedOperationException("unhandled state=" + state); 113 } 114 } 115 116 @Override 117 protected void rollbackState(MasterProcedureEnv env, TState state) 118 throws IOException, InterruptedException { 119 throw new UnsupportedOperationException(); 120 } 121 122 @Override 123 protected synchronized boolean setTimeoutFailure(MasterProcedureEnv env) { 124 setState(ProcedureProtos.ProcedureState.RUNNABLE); 125 env.getProcedureScheduler().addFront(this); 126 return false; 127 } 128 129 /** 130 * We have two state for this type of procedures, the initial state for scheduling unassign 131 * procedures, and the confirm state for checking whether we have unassigned all the regions. 132 * @return the confirm state 133 */ 134 protected abstract TState getConfirmState(); 135 136 /** 137 * Submit TRSP for unassigning regions. Return the number of regions in RIT state that we can not 138 * schedule TRSP for them. 139 */ 140 protected abstract int submitUnassignProcedure(MasterProcedureEnv env, 141 Consumer<TransitRegionStateProcedure> submit); 142 143 /** 144 * Return the number of uncloses regions. Returning {@code 0} means we are done. 145 */ 146 protected abstract int numberOfUnclosedRegions(MasterProcedureEnv env); 147}