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.fs.Path; 022import org.apache.hadoop.hbase.HBaseIOException; 023import org.apache.hadoop.hbase.TableName; 024import org.apache.hadoop.hbase.client.RegionInfo; 025import org.apache.hadoop.hbase.master.MasterCoprocessorHost; 026import org.apache.hadoop.hbase.master.MasterFileSystem; 027import org.apache.hadoop.hbase.master.assignment.RegionStateNode; 028import org.apache.hadoop.hbase.master.assignment.TransitRegionStateProcedure; 029import org.apache.hadoop.hbase.regionserver.HRegionFileSystem; 030import org.apache.hadoop.hbase.util.CommonFSUtils; 031import org.apache.yetus.audience.InterfaceAudience; 032import org.slf4j.Logger; 033import org.slf4j.LoggerFactory; 034 035import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProcedureProtos.TruncateRegionState; 036 037@InterfaceAudience.Private 038public class TruncateRegionProcedure 039 extends AbstractStateMachineRegionProcedure<TruncateRegionState> { 040 private static final Logger LOG = LoggerFactory.getLogger(TruncateRegionProcedure.class); 041 042 @SuppressWarnings("unused") 043 public TruncateRegionProcedure() { 044 // Required by the Procedure framework to create the procedure on replay 045 super(); 046 } 047 048 public TruncateRegionProcedure(final MasterProcedureEnv env, final RegionInfo hri) 049 throws HBaseIOException { 050 super(env, hri); 051 checkOnline(env, getRegion()); 052 } 053 054 public TruncateRegionProcedure(final MasterProcedureEnv env, final RegionInfo region, 055 ProcedurePrepareLatch latch) throws HBaseIOException { 056 super(env, region, latch); 057 preflightChecks(env, true); 058 } 059 060 @Override 061 protected Flow executeFromState(final MasterProcedureEnv env, TruncateRegionState state) 062 throws InterruptedException { 063 if (LOG.isTraceEnabled()) { 064 LOG.trace(this + " execute state=" + state); 065 } 066 try { 067 switch (state) { 068 case TRUNCATE_REGION_PRE_OPERATION: 069 if (!prepareTruncate()) { 070 assert isFailed() : "the truncate should have an exception here"; 071 return Flow.NO_MORE_STATE; 072 } 073 checkOnline(env, getRegion()); 074 assert getRegion().getReplicaId() == RegionInfo.DEFAULT_REPLICA_ID || isFailed() 075 : "Can't truncate replicas directly. " 076 + "Replicas are auto-truncated when their primary is truncated."; 077 preTruncate(env); 078 setNextState(TruncateRegionState.TRUNCATE_REGION_MAKE_OFFLINE); 079 break; 080 case TRUNCATE_REGION_MAKE_OFFLINE: 081 addChildProcedure(createUnAssignProcedures(env)); 082 setNextState(TruncateRegionState.TRUNCATE_REGION_REMOVE); 083 break; 084 case TRUNCATE_REGION_REMOVE: 085 deleteRegionFromFileSystem(env); 086 setNextState(TruncateRegionState.TRUNCATE_REGION_MAKE_ONLINE); 087 break; 088 case TRUNCATE_REGION_MAKE_ONLINE: 089 addChildProcedure(createAssignProcedures(env)); 090 setNextState(TruncateRegionState.TRUNCATE_REGION_POST_OPERATION); 091 break; 092 case TRUNCATE_REGION_POST_OPERATION: 093 postTruncate(env); 094 LOG.debug("truncate '" + getTableName() + "' completed"); 095 return Flow.NO_MORE_STATE; 096 default: 097 throw new UnsupportedOperationException("unhandled state=" + state); 098 } 099 } catch (IOException e) { 100 if (isRollbackSupported(state)) { 101 setFailure("master-truncate-region", e); 102 } else { 103 LOG.warn("Retriable error trying to truncate region=" + getRegion().getRegionNameAsString() 104 + " state=" + state, e); 105 } 106 } 107 return Flow.HAS_MORE_STATE; 108 } 109 110 private void deleteRegionFromFileSystem(final MasterProcedureEnv env) throws IOException { 111 RegionStateNode regionNode = 112 env.getAssignmentManager().getRegionStates().getRegionStateNode(getRegion()); 113 try { 114 regionNode.lock(); 115 final MasterFileSystem mfs = env.getMasterServices().getMasterFileSystem(); 116 final Path tableDir = CommonFSUtils.getTableDir(mfs.getRootDir(), getTableName()); 117 HRegionFileSystem.deleteRegionFromFileSystem(env.getMasterConfiguration(), 118 mfs.getFileSystem(), tableDir, getRegion()); 119 } finally { 120 regionNode.unlock(); 121 } 122 } 123 124 @Override 125 protected void rollbackState(final MasterProcedureEnv env, final TruncateRegionState state) 126 throws IOException { 127 if (state == TruncateRegionState.TRUNCATE_REGION_PRE_OPERATION) { 128 // Nothing to rollback, pre-truncate is just table-state checks. 129 return; 130 } 131 if (state == TruncateRegionState.TRUNCATE_REGION_MAKE_OFFLINE) { 132 RegionStateNode regionNode = 133 env.getAssignmentManager().getRegionStates().getRegionStateNode(getRegion()); 134 if (regionNode == null) { 135 // Region was unassigned by state TRUNCATE_REGION_MAKE_OFFLINE. 136 // So Assign it back 137 addChildProcedure(createAssignProcedures(env)); 138 } 139 return; 140 } 141 // The truncate doesn't have a rollback. The execution will succeed, at some point. 142 throw new UnsupportedOperationException("unhandled state=" + state); 143 } 144 145 @Override 146 protected void completionCleanup(final MasterProcedureEnv env) { 147 releaseSyncLatch(); 148 } 149 150 @Override 151 protected boolean isRollbackSupported(final TruncateRegionState state) { 152 switch (state) { 153 case TRUNCATE_REGION_PRE_OPERATION: 154 return true; 155 case TRUNCATE_REGION_MAKE_OFFLINE: 156 return true; 157 default: 158 return false; 159 } 160 } 161 162 @Override 163 protected TruncateRegionState getState(final int stateId) { 164 return TruncateRegionState.forNumber(stateId); 165 } 166 167 @Override 168 protected int getStateId(final TruncateRegionState state) { 169 return state.getNumber(); 170 } 171 172 @Override 173 protected TruncateRegionState getInitialState() { 174 return TruncateRegionState.TRUNCATE_REGION_PRE_OPERATION; 175 } 176 177 @Override 178 public void toStringClassDetails(StringBuilder sb) { 179 sb.append(getClass().getSimpleName()); 180 sb.append(" (region="); 181 sb.append(getRegion().getRegionNameAsString()); 182 sb.append(")"); 183 } 184 185 private boolean prepareTruncate() throws IOException { 186 if (getTableName().equals(TableName.META_TABLE_NAME)) { 187 throw new IOException("Can't truncate region in catalog tables"); 188 } 189 return true; 190 } 191 192 private void preTruncate(final MasterProcedureEnv env) throws IOException { 193 final MasterCoprocessorHost cpHost = env.getMasterCoprocessorHost(); 194 if (cpHost != null) { 195 cpHost.preTruncateRegionAction(getRegion(), getUser()); 196 } 197 } 198 199 private void postTruncate(final MasterProcedureEnv env) throws IOException { 200 final MasterCoprocessorHost cpHost = env.getMasterCoprocessorHost(); 201 if (cpHost != null) { 202 cpHost.postTruncateRegionAction(getRegion(), getUser()); 203 } 204 } 205 206 @Override 207 public TableOperationType getTableOperationType() { 208 return TableOperationType.REGION_TRUNCATE; 209 } 210 211 private TransitRegionStateProcedure createUnAssignProcedures(MasterProcedureEnv env) 212 throws IOException { 213 return env.getAssignmentManager().createOneUnassignProcedure(getRegion(), true); 214 } 215 216 private TransitRegionStateProcedure createAssignProcedures(MasterProcedureEnv env) { 217 return env.getAssignmentManager().createOneAssignProcedure(getRegion(), true); 218 } 219}