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.List;
022import java.util.concurrent.Callable;
023import java.util.stream.Collectors;
024
025import org.apache.hadoop.conf.Configuration;
026import org.apache.hadoop.hbase.ipc.RpcControllerFactory;
027import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil;
028import org.apache.hadoop.hbase.shaded.protobuf.RequestConverter;
029import org.apache.hadoop.hbase.shaded.protobuf.generated.HBaseProtos;
030import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos;
031import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos.GetTableStateResponse;
032import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos.HbckService.BlockingInterface;
033import org.apache.hbase.thirdparty.com.google.protobuf.ServiceException;
034import org.apache.yetus.audience.InterfaceAudience;
035import org.slf4j.Logger;
036import org.slf4j.LoggerFactory;
037
038
039/**
040 * Use {@link ClusterConnection#getHbck()} to obtain an instance of {@link Hbck} instead of
041 * constructing
042 * an HBaseHbck directly. This will be mostly used by hbck tool.
043 *
044 * <p>Connection should be an <i>unmanaged</i> connection obtained via
045 * {@link ConnectionFactory#createConnection(Configuration)}.</p>
046 *
047 * <p>An instance of this class is lightweight and not-thread safe. A new instance should be created
048 * by each thread. Pooling or caching of the instance is not recommended.</p>
049 *
050 * @see ConnectionFactory
051 * @see ClusterConnection
052 * @see Hbck
053 */
054@InterfaceAudience.Private
055public class HBaseHbck implements Hbck {
056  private static final Logger LOG = LoggerFactory.getLogger(HBaseHbck.class);
057
058  private boolean aborted;
059  private final BlockingInterface hbck;
060
061  private RpcControllerFactory rpcControllerFactory;
062
063  HBaseHbck(ClusterConnection connection, BlockingInterface hbck) throws IOException {
064    this.hbck = hbck;
065    this.rpcControllerFactory = connection.getRpcControllerFactory();
066  }
067
068  @Override
069  public void close() throws IOException {
070    // currently does nothing
071  }
072
073  @Override
074  public void abort(String why, Throwable e) {
075    this.aborted = true;
076    // Currently does nothing but throw the passed message and exception
077    throw new RuntimeException(why, e);
078  }
079
080  @Override
081  public boolean isAborted() {
082    return this.aborted;
083  }
084
085  /**
086   * NOTE: This is a dangerous action, as existing running procedures for the table or regions
087   * which belong to the table may get confused.
088   */
089  @Override
090  public TableState setTableStateInMeta(TableState state) throws IOException {
091    try {
092      GetTableStateResponse response = hbck.setTableStateInMeta(
093          rpcControllerFactory.newController(),
094          RequestConverter.buildSetTableStateInMetaRequest(state));
095      return TableState.convert(state.getTableName(), response.getTableState());
096    } catch (ServiceException se) {
097      LOG.debug("table={}, state={}", state.getTableName(), state.getState(), se);
098      throw new IOException(se);
099    }
100  }
101
102  @Override
103  public List<Long> assigns(List<String> encodedRegionNames, boolean override)
104      throws IOException {
105    try {
106      MasterProtos.AssignsResponse response =
107          this.hbck.assigns(rpcControllerFactory.newController(),
108              RequestConverter.toAssignRegionsRequest(encodedRegionNames, override));
109      return response.getPidList();
110    } catch (ServiceException se) {
111      LOG.debug(toCommaDelimitedString(encodedRegionNames), se);
112      throw new IOException(se);
113    }
114  }
115
116  @Override
117  public List<Long> unassigns(List<String> encodedRegionNames, boolean override)
118      throws IOException {
119    try {
120      MasterProtos.UnassignsResponse response =
121          this.hbck.unassigns(rpcControllerFactory.newController(),
122              RequestConverter.toUnassignRegionsRequest(encodedRegionNames, override));
123      return response.getPidList();
124    } catch (ServiceException se) {
125      LOG.debug(toCommaDelimitedString(encodedRegionNames), se);
126      throw new IOException(se);
127    }
128  }
129
130  private static String toCommaDelimitedString(List<String> list) {
131    return list.stream().collect(Collectors.joining(", "));
132  }
133
134  @Override
135  public List<Boolean> bypassProcedure(List<Long> pids, long waitTime, boolean override,
136      boolean recursive)
137      throws IOException {
138    MasterProtos.BypassProcedureResponse response = ProtobufUtil.call(
139        new Callable<MasterProtos.BypassProcedureResponse>() {
140          @Override
141          public MasterProtos.BypassProcedureResponse call() throws Exception {
142            try {
143              return hbck.bypassProcedure(rpcControllerFactory.newController(),
144                  MasterProtos.BypassProcedureRequest.newBuilder().addAllProcId(pids).
145                      setWaitTime(waitTime).setOverride(override).setRecursive(recursive).build());
146            } catch (Throwable t) {
147              LOG.error(pids.stream().map(i -> i.toString()).
148                  collect(Collectors.joining(", ")), t);
149              throw t;
150            }
151          }
152        });
153    return response.getBypassedList();
154  }
155
156  @Override
157  public List<Long> scheduleServerCrashProcedure(List<HBaseProtos.ServerName> serverNames)
158      throws IOException {
159    try {
160      MasterProtos.ScheduleServerCrashProcedureResponse response =
161          this.hbck.scheduleServerCrashProcedure(rpcControllerFactory.newController(),
162            RequestConverter.toScheduleServerCrashProcedureRequest(serverNames));
163      return response.getPidList();
164    } catch (ServiceException se) {
165      LOG.debug(toCommaDelimitedString(
166        serverNames.stream().map(serverName -> ProtobufUtil.toServerName(serverName).toString())
167            .collect(Collectors.toList())),
168        se);
169      throw new IOException(se);
170    }
171  }
172}