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;
019import java.io.IOException;
020import org.apache.hadoop.fs.Path;
021import org.apache.hadoop.hbase.ServerName;
022import org.apache.hadoop.hbase.master.SplitWALManager;
023import org.apache.hadoop.hbase.procedure2.ProcedureStateSerializer;
024import org.apache.hadoop.hbase.procedure2.ProcedureSuspendedException;
025import org.apache.hadoop.hbase.procedure2.ProcedureUtil;
026import org.apache.hadoop.hbase.procedure2.ProcedureYieldException;
027import org.apache.hadoop.hbase.procedure2.StateMachineProcedure;
028import org.apache.hadoop.hbase.util.RetryCounter;
029import org.apache.hadoop.hbase.wal.AbstractFSWALProvider;
030import org.apache.yetus.audience.InterfaceAudience;
031import org.slf4j.Logger;
032import org.slf4j.LoggerFactory;
033import org.apache.hbase.thirdparty.com.google.common.annotations.VisibleForTesting;
034import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil;
035import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProcedureProtos;
036import org.apache.hadoop.hbase.shaded.protobuf.generated.ProcedureProtos;
037
038/**
039 * The procedure is to split a WAL. It will get an available region server and
040 * schedule a {@link SplitWALRemoteProcedure} to actually send the request to region
041 * server to split this WAL.
042 * It also check if the split wal task really succeed. If the WAL still exists, it will
043 * schedule another region server to split this WAL.
044 */
045@InterfaceAudience.Private
046public class SplitWALProcedure
047    extends StateMachineProcedure<MasterProcedureEnv, MasterProcedureProtos.SplitWALState>
048    implements ServerProcedureInterface {
049  private static final Logger LOG = LoggerFactory.getLogger(SplitWALProcedure.class);
050  private String walPath;
051  private ServerName worker;
052  private ServerName crashedServer;
053  private RetryCounter retryCounter;
054
055  public SplitWALProcedure() {
056  }
057
058  public SplitWALProcedure(String walPath, ServerName crashedServer) {
059    this.walPath = walPath;
060    this.crashedServer = crashedServer;
061  }
062
063  @Override
064  protected Flow executeFromState(MasterProcedureEnv env, MasterProcedureProtos.SplitWALState state)
065      throws ProcedureSuspendedException, ProcedureYieldException, InterruptedException {
066    SplitWALManager splitWALManager = env.getMasterServices().getSplitWALManager();
067    switch (state) {
068      case ACQUIRE_SPLIT_WAL_WORKER:
069        worker = splitWALManager.acquireSplitWALWorker(this);
070        setNextState(MasterProcedureProtos.SplitWALState.DISPATCH_WAL_TO_WORKER);
071        return Flow.HAS_MORE_STATE;
072      case DISPATCH_WAL_TO_WORKER:
073        assert worker != null;
074        addChildProcedure(new SplitWALRemoteProcedure(worker, crashedServer, walPath));
075        setNextState(MasterProcedureProtos.SplitWALState.RELEASE_SPLIT_WORKER);
076        return Flow.HAS_MORE_STATE;
077      case RELEASE_SPLIT_WORKER:
078        boolean finished;
079        try {
080          finished = splitWALManager.isSplitWALFinished(walPath);
081        } catch (IOException ioe) {
082          if (retryCounter == null) {
083            retryCounter = ProcedureUtil.createRetryCounter(env.getMasterConfiguration());
084          }
085          long backoff = retryCounter.getBackoffTimeAndIncrementAttempts();
086          LOG.warn("Failed to check whether splitting wal {} success, wait {} seconds to retry",
087            walPath, backoff / 1000, ioe);
088          setTimeout(Math.toIntExact(backoff));
089          setState(ProcedureProtos.ProcedureState.WAITING_TIMEOUT);
090          skipPersistence();
091          throw new ProcedureSuspendedException();
092        }
093        splitWALManager.releaseSplitWALWorker(worker, env.getProcedureScheduler());
094        if (!finished) {
095          LOG.warn("Failed to split wal {} by server {}, retry...", walPath, worker);
096          setNextState(MasterProcedureProtos.SplitWALState.ACQUIRE_SPLIT_WAL_WORKER);
097          return Flow.HAS_MORE_STATE;
098        }
099        ServerCrashProcedure.updateProgress(env, getParentProcId());
100        return Flow.NO_MORE_STATE;
101      default:
102        throw new UnsupportedOperationException("unhandled state=" + state);
103    }
104  }
105
106  @Override
107  protected void rollbackState(MasterProcedureEnv env,
108      MasterProcedureProtos.SplitWALState splitOneWalState)
109      throws IOException, InterruptedException {
110    if (splitOneWalState == getInitialState()) {
111      return;
112    }
113    throw new UnsupportedOperationException();
114  }
115
116  @Override
117  protected MasterProcedureProtos.SplitWALState getState(int stateId) {
118    return MasterProcedureProtos.SplitWALState.forNumber(stateId);
119  }
120
121  @Override
122  protected int getStateId(MasterProcedureProtos.SplitWALState state) {
123    return state.getNumber();
124  }
125
126  @Override
127  protected MasterProcedureProtos.SplitWALState getInitialState() {
128    return MasterProcedureProtos.SplitWALState.ACQUIRE_SPLIT_WAL_WORKER;
129  }
130
131  @Override
132  protected void serializeStateData(ProcedureStateSerializer serializer) throws IOException {
133    super.serializeStateData(serializer);
134    MasterProcedureProtos.SplitWALData.Builder builder =
135        MasterProcedureProtos.SplitWALData.newBuilder();
136    builder.setWalPath(walPath).setCrashedServer(ProtobufUtil.toServerName(crashedServer));
137    if (worker != null) {
138      builder.setWorker(ProtobufUtil.toServerName(worker));
139    }
140    serializer.serialize(builder.build());
141  }
142
143  @Override
144  protected void deserializeStateData(ProcedureStateSerializer serializer) throws IOException {
145    super.deserializeStateData(serializer);
146    MasterProcedureProtos.SplitWALData data =
147        serializer.deserialize(MasterProcedureProtos.SplitWALData.class);
148    walPath = data.getWalPath();
149    crashedServer = ProtobufUtil.toServerName(data.getCrashedServer());
150    if (data.hasWorker()) {
151      worker = ProtobufUtil.toServerName(data.getWorker());
152    }
153  }
154
155  @Override
156  protected synchronized boolean setTimeoutFailure(MasterProcedureEnv env) {
157    setState(ProcedureProtos.ProcedureState.RUNNABLE);
158    env.getProcedureScheduler().addFront(this);
159    return false;
160  }
161
162  public String getWAL() {
163    return walPath;
164  }
165
166  @VisibleForTesting
167  public ServerName getWorker(){
168    return worker;
169  }
170
171  @Override
172  public ServerName getServerName() {
173    return this.crashedServer;
174  }
175
176  @Override
177  public boolean hasMetaTableRegion() {
178    return AbstractFSWALProvider.isMetaFile(new Path(walPath));
179  }
180
181  @Override
182  public ServerOperationType getServerOperationType() {
183    return ServerOperationType.SPLIT_WAL;
184  }
185
186  @Override
187  protected void afterReplay(MasterProcedureEnv env){
188    if (worker != null) {
189      if (env != null && env.getMasterServices() != null &&
190          env.getMasterServices().getSplitWALManager() != null) {
191        env.getMasterServices().getSplitWALManager().addUsedSplitWALWorker(worker);
192      }
193    }
194  }
195
196  @Override protected void toStringClassDetails(StringBuilder builder) {
197    builder.append(getProcName());
198    if (this.worker != null) {
199      builder.append(", worker=");
200      builder.append(this.worker);
201    }
202    if (this.retryCounter != null) {
203      builder.append(", retry=");
204      builder.append(this.retryCounter);
205    }
206  }
207
208  @Override public String getProcName() {
209    return getClass().getSimpleName() + " " + getWALNameFromStrPath(getWAL());
210  }
211
212  /**
213   * @return Return the WAL filename when given a Path-as-a-string; i.e. return the last path
214   *   component only.
215   */
216  static String getWALNameFromStrPath(String path) {
217    int slashIndex = path.lastIndexOf('/');
218    return slashIndex != -1? path.substring(slashIndex + 1): path;
219  }
220}