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