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