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.client;
019
020import java.io.IOException;
021import java.util.HashMap;
022import java.util.List;
023import java.util.Map;
024import java.util.concurrent.Callable;
025import java.util.stream.Collectors;
026import org.apache.hadoop.conf.Configuration;
027import org.apache.hadoop.hbase.ServerName;
028import org.apache.hadoop.hbase.ipc.RpcControllerFactory;
029import org.apache.hadoop.hbase.master.RegionState;
030import org.apache.yetus.audience.InterfaceAudience;
031import org.slf4j.Logger;
032import org.slf4j.LoggerFactory;
033
034import org.apache.hbase.thirdparty.com.google.protobuf.ServiceException;
035
036import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil;
037import org.apache.hadoop.hbase.shaded.protobuf.RequestConverter;
038import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos;
039import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos.AssignsResponse;
040import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos.BypassProcedureRequest;
041import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos.BypassProcedureResponse;
042import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos.FixMetaRequest;
043import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos.GetTableStateResponse;
044import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos.HbckService.BlockingInterface;
045import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos.RegionSpecifierAndState;
046import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos.RunHbckChoreRequest;
047import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos.RunHbckChoreResponse;
048import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos.ScheduleSCPsForUnknownServersRequest;
049import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos.ScheduleSCPsForUnknownServersResponse;
050import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos.ScheduleServerCrashProcedureResponse;
051import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos.UnassignsResponse;
052
053/**
054 * Use {@link Connection#getHbck()} to obtain an instance of {@link Hbck} instead of constructing an
055 * HBaseHbck directly.
056 * <p>
057 * Connection should be an <i>unmanaged</i> connection obtained via
058 * {@link ConnectionFactory#createConnection(Configuration)}.
059 * </p>
060 * <p>
061 * NOTE: The methods in here can do damage to a cluster if applied in the wrong sequence or at the
062 * wrong time. Use with caution. For experts only. These methods are only for the extreme case where
063 * the cluster has been damaged or has achieved an inconsistent state because of some unforeseen
064 * circumstance or bug and requires manual intervention.
065 * <p>
066 * An instance of this class is lightweight and not-thread safe. A new instance should be created by
067 * each thread. Pooling or caching of the instance is not recommended.
068 * </p>
069 * @see ConnectionFactory
070 * @see Hbck
071 */
072@InterfaceAudience.Private
073public class HBaseHbck implements Hbck {
074  private static final Logger LOG = LoggerFactory.getLogger(HBaseHbck.class);
075
076  private boolean aborted;
077  private final BlockingInterface hbck;
078
079  private RpcControllerFactory rpcControllerFactory;
080
081  HBaseHbck(BlockingInterface hbck, RpcControllerFactory rpcControllerFactory) {
082    this.hbck = hbck;
083    this.rpcControllerFactory = rpcControllerFactory;
084  }
085
086  @Override
087  public void close() throws IOException {
088    // currently does nothing
089  }
090
091  @Override
092  public void abort(String why, Throwable e) {
093    this.aborted = true;
094    // Currently does nothing but throw the passed message and exception
095    throw new RuntimeException(why, e);
096  }
097
098  @Override
099  public boolean isAborted() {
100    return this.aborted;
101  }
102
103  @Override
104  public TableState setTableStateInMeta(TableState state) throws IOException {
105    try {
106      GetTableStateResponse response =
107        hbck.setTableStateInMeta(rpcControllerFactory.newController(),
108          RequestConverter.buildSetTableStateInMetaRequest(state));
109      return TableState.convert(state.getTableName(), response.getTableState());
110    } catch (ServiceException se) {
111      LOG.debug("table={}, state={}", state.getTableName(), state.getState(), se);
112      throw new IOException(se);
113    }
114  }
115
116  @Override
117  public Map<String, RegionState.State> setRegionStateInMeta(
118    Map<String, RegionState.State> nameOrEncodedName2State) throws IOException {
119    try {
120      if (LOG.isDebugEnabled()) {
121        nameOrEncodedName2State.forEach((k, v) -> LOG.debug("region={}, state={}", k, v));
122      }
123      MasterProtos.SetRegionStateInMetaResponse response =
124        hbck.setRegionStateInMeta(rpcControllerFactory.newController(),
125          RequestConverter.buildSetRegionStateInMetaRequest(nameOrEncodedName2State));
126      Map<String, RegionState.State> result = new HashMap<>();
127      for (RegionSpecifierAndState nameAndState : response.getStatesList()) {
128        result.put(nameAndState.getRegionSpecifier().getValue().toStringUtf8(),
129          RegionState.State.convert(nameAndState.getState()));
130      }
131      return result;
132    } catch (ServiceException se) {
133      throw new IOException(se);
134    }
135  }
136
137  @Override
138  public List<Long> assigns(List<String> encodedRegionNames, boolean override) throws IOException {
139    try {
140      AssignsResponse response = this.hbck.assigns(rpcControllerFactory.newController(),
141        RequestConverter.toAssignRegionsRequest(encodedRegionNames, override));
142      return response.getPidList();
143    } catch (ServiceException se) {
144      LOG.debug(toCommaDelimitedString(encodedRegionNames), se);
145      throw new IOException(se);
146    }
147  }
148
149  @Override
150  public List<Long> unassigns(List<String> encodedRegionNames, boolean override)
151    throws IOException {
152    try {
153      UnassignsResponse response = this.hbck.unassigns(rpcControllerFactory.newController(),
154        RequestConverter.toUnassignRegionsRequest(encodedRegionNames, override));
155      return response.getPidList();
156    } catch (ServiceException se) {
157      LOG.debug(toCommaDelimitedString(encodedRegionNames), se);
158      throw new IOException(se);
159    }
160  }
161
162  private static String toCommaDelimitedString(List<String> list) {
163    return list.stream().collect(Collectors.joining(", "));
164  }
165
166  @Override
167  public List<Boolean> bypassProcedure(List<Long> pids, long waitTime, boolean override,
168    boolean recursive) throws IOException {
169    BypassProcedureResponse response = ProtobufUtil.call(new Callable<BypassProcedureResponse>() {
170      @Override
171      public BypassProcedureResponse call() throws Exception {
172        try {
173          return hbck.bypassProcedure(rpcControllerFactory.newController(),
174            BypassProcedureRequest.newBuilder().addAllProcId(pids).setWaitTime(waitTime)
175              .setOverride(override).setRecursive(recursive).build());
176        } catch (Throwable t) {
177          LOG.error(pids.stream().map(i -> i.toString()).collect(Collectors.joining(", ")), t);
178          throw t;
179        }
180      }
181    });
182    return response.getBypassedList();
183  }
184
185  @Override
186  public List<Long> scheduleServerCrashProcedures(List<ServerName> serverNames) throws IOException {
187    try {
188      ScheduleServerCrashProcedureResponse response =
189        this.hbck.scheduleServerCrashProcedure(rpcControllerFactory.newController(),
190          RequestConverter.toScheduleServerCrashProcedureRequest(serverNames));
191      return response.getPidList();
192    } catch (ServiceException se) {
193      LOG.debug(toCommaDelimitedString(
194        serverNames.stream().map(serverName -> ProtobufUtil.toServerName(serverName).toString())
195          .collect(Collectors.toList())),
196        se);
197      throw new IOException(se);
198    }
199  }
200
201  @Override
202  public List<Long> scheduleSCPsForUnknownServers() throws IOException {
203    try {
204      ScheduleSCPsForUnknownServersResponse response =
205        this.hbck.scheduleSCPsForUnknownServers(rpcControllerFactory.newController(),
206          ScheduleSCPsForUnknownServersRequest.newBuilder().build());
207      return response.getPidList();
208    } catch (ServiceException se) {
209      LOG.debug("Failed to run ServerCrashProcedures for unknown servers", se);
210      throw new IOException(se);
211    }
212  }
213
214  @Override
215  public boolean runHbckChore() throws IOException {
216    try {
217      RunHbckChoreResponse response = this.hbck.runHbckChore(rpcControllerFactory.newController(),
218        RunHbckChoreRequest.newBuilder().build());
219      return response.getRan();
220    } catch (ServiceException se) {
221      LOG.debug("Failed to run HBCK chore", se);
222      throw new IOException(se);
223    }
224  }
225
226  @Override
227  public void fixMeta() throws IOException {
228    try {
229      this.hbck.fixMeta(rpcControllerFactory.newController(), FixMetaRequest.newBuilder().build());
230    } catch (ServiceException se) {
231      throw new IOException(se);
232    }
233  }
234}