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.assignment; 019 020import java.io.IOException; 021import java.util.List; 022import java.util.stream.Collectors; 023import org.apache.hadoop.hbase.MetaTableAccessor; 024import org.apache.hadoop.hbase.TableName; 025import org.apache.hadoop.hbase.client.RegionInfo; 026import org.apache.hadoop.hbase.master.procedure.AbstractStateMachineTableProcedure; 027import org.apache.hadoop.hbase.master.procedure.MasterProcedureEnv; 028import org.apache.hadoop.hbase.procedure2.ProcedureStateSerializer; 029import org.apache.hadoop.hbase.procedure2.ProcedureSuspendedException; 030import org.apache.hadoop.hbase.procedure2.ProcedureYieldException; 031import org.slf4j.Logger; 032import org.slf4j.LoggerFactory; 033 034import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil; 035import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProcedureProtos.GCMergedRegionsState; 036import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProcedureProtos.GCMultipleMergedRegionsStateData; 037 038/** 039 * GC regions that have been Merged. Caller determines if it is GC time. This Procedure does not 040 * check. This is a Table Procedure. We take a read lock on the Table. We do NOT keep a lock for the 041 * life of this procedure. The sub-procedures take locks on the Regions they are purging. Replaces a 042 * Procedure that did two regions only at a time instead doing multiple merges in the one go; only 043 * difference from the old {@link GCMergedRegionsState} is the serialization; this class has a 044 * different serialization profile writing out more than just two regions. 045 */ 046@org.apache.yetus.audience.InterfaceAudience.Private 047public class GCMultipleMergedRegionsProcedure 048 extends AbstractStateMachineTableProcedure<GCMergedRegionsState> { 049 private static final Logger LOG = LoggerFactory.getLogger(GCMultipleMergedRegionsProcedure.class); 050 private List<RegionInfo> parents; 051 private RegionInfo mergedChild; 052 053 public GCMultipleMergedRegionsProcedure(final MasterProcedureEnv env, 054 final RegionInfo mergedChild, final List<RegionInfo> parents) { 055 super(env); 056 this.parents = parents; 057 this.mergedChild = mergedChild; 058 } 059 060 public GCMultipleMergedRegionsProcedure() { 061 // Required by the Procedure framework to create the procedure on replay 062 super(); 063 } 064 065 @Override 066 protected boolean holdLock(MasterProcedureEnv env) { 067 return true; 068 } 069 070 @Override 071 protected LockState acquireLock(final MasterProcedureEnv env) { 072 // It now takes an exclusive lock on the merged child region to make sure 073 // that no two parallel running of two GCMultipleMergedRegionsProcedures on the 074 // region. 075 if (env.getProcedureScheduler().waitRegion(this, mergedChild)) { 076 return LockState.LOCK_EVENT_WAIT; 077 } 078 return LockState.LOCK_ACQUIRED; 079 } 080 081 @Override 082 protected void releaseLock(final MasterProcedureEnv env) { 083 env.getProcedureScheduler().wakeRegion(this, mergedChild); 084 } 085 086 @Override 087 public TableOperationType getTableOperationType() { 088 return TableOperationType.MERGED_REGIONS_GC; 089 } 090 091 @Override 092 protected Flow executeFromState(MasterProcedureEnv env, GCMergedRegionsState state) 093 throws ProcedureSuspendedException, ProcedureYieldException, InterruptedException { 094 if (LOG.isTraceEnabled()) { 095 LOG.trace(this + " execute state=" + state); 096 } 097 try { 098 switch (state) { 099 case GC_MERGED_REGIONS_PREPARE: 100 // If GCMultipleMergedRegionsProcedure processing is slower than the CatalogJanitor's scan 101 // interval, it will end resubmitting GCMultipleMergedRegionsProcedure for the same 102 // region. We can skip duplicate GCMultipleMergedRegionsProcedure while previous finished 103 List<RegionInfo> parents = 104 MetaTableAccessor.getMergeRegions(env.getMasterServices().getConnection(), mergedChild); 105 if (parents == null || parents.isEmpty()) { 106 LOG.info("{} mergeXXX qualifiers have ALL been deleted", 107 mergedChild.getShortNameToLog()); 108 return Flow.NO_MORE_STATE; 109 } 110 setNextState(GCMergedRegionsState.GC_MERGED_REGIONS_PURGE); 111 break; 112 case GC_MERGED_REGIONS_PURGE: 113 addChildProcedure(createGCRegionProcedures(env)); 114 setNextState(GCMergedRegionsState.GC_REGION_EDIT_METADATA); 115 break; 116 case GC_REGION_EDIT_METADATA: 117 MetaTableAccessor.deleteMergeQualifiers(env.getMasterServices().getConnection(), 118 mergedChild); 119 return Flow.NO_MORE_STATE; 120 default: 121 throw new UnsupportedOperationException(this + " unhandled state=" + state); 122 } 123 } catch (IOException ioe) { 124 // TODO: This is going to spew log? 125 LOG.warn("Error trying to GC merged regions {}; retrying...", this.parents.stream() 126 .map(r -> RegionInfo.getShortNameToLog(r)).collect(Collectors.joining(", ")), ioe); 127 } 128 return Flow.HAS_MORE_STATE; 129 } 130 131 private GCRegionProcedure[] createGCRegionProcedures(final MasterProcedureEnv env) { 132 GCRegionProcedure[] procs = new GCRegionProcedure[this.parents.size()]; 133 int index = 0; 134 for (RegionInfo ri : this.parents) { 135 GCRegionProcedure proc = new GCRegionProcedure(env, ri); 136 proc.setOwner(env.getRequestUser().getShortName()); 137 procs[index++] = proc; 138 } 139 return procs; 140 } 141 142 @Override 143 protected void rollbackState(MasterProcedureEnv env, GCMergedRegionsState state) 144 throws IOException, InterruptedException { 145 // no-op 146 } 147 148 @Override 149 protected GCMergedRegionsState getState(int stateId) { 150 return GCMergedRegionsState.forNumber(stateId); 151 } 152 153 @Override 154 protected int getStateId(GCMergedRegionsState state) { 155 return state.getNumber(); 156 } 157 158 @Override 159 protected GCMergedRegionsState getInitialState() { 160 return GCMergedRegionsState.GC_MERGED_REGIONS_PREPARE; 161 } 162 163 @Override 164 protected void serializeStateData(ProcedureStateSerializer serializer) throws IOException { 165 super.serializeStateData(serializer); 166 final GCMultipleMergedRegionsStateData.Builder msg = 167 GCMultipleMergedRegionsStateData.newBuilder() 168 .addAllParents( 169 this.parents.stream().map(ProtobufUtil::toRegionInfo).collect(Collectors.toList())) 170 .setMergedChild(ProtobufUtil.toRegionInfo(this.mergedChild)); 171 serializer.serialize(msg.build()); 172 } 173 174 @Override 175 protected void deserializeStateData(ProcedureStateSerializer serializer) throws IOException { 176 super.deserializeStateData(serializer); 177 final GCMultipleMergedRegionsStateData msg = 178 serializer.deserialize(GCMultipleMergedRegionsStateData.class); 179 this.parents = 180 msg.getParentsList().stream().map(ProtobufUtil::toRegionInfo).collect(Collectors.toList()); 181 this.mergedChild = ProtobufUtil.toRegionInfo(msg.getMergedChild()); 182 } 183 184 @Override 185 public void toStringClassDetails(StringBuilder sb) { 186 sb.append(getClass().getSimpleName()); 187 sb.append(" child="); 188 sb.append(this.mergedChild.getShortNameToLog()); 189 sb.append(", parents:"); 190 sb.append(this.parents.stream().map(r -> RegionInfo.getShortNameToLog(r)) 191 .collect(Collectors.joining(", "))); 192 } 193 194 @Override 195 public TableName getTableName() { 196 return this.mergedChild.getTable(); 197 } 198}