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