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.util;
019
020import java.io.IOException;
021import java.util.Collection;
022import java.util.EnumSet;
023import java.util.List;
024import java.util.Random;
025import java.util.concurrent.ThreadLocalRandom;
026import org.apache.hadoop.conf.Configuration;
027import org.apache.hadoop.fs.Path;
028import org.apache.hadoop.hbase.ClusterMetrics.Option;
029import org.apache.hadoop.hbase.MetaTableAccessor;
030import org.apache.hadoop.hbase.ServerName;
031import org.apache.hadoop.hbase.TableName;
032import org.apache.hadoop.hbase.ZooKeeperConnectionException;
033import org.apache.hadoop.hbase.client.Admin;
034import org.apache.hadoop.hbase.client.AsyncClusterConnection;
035import org.apache.hadoop.hbase.client.ClusterConnectionFactory;
036import org.apache.hadoop.hbase.client.Connection;
037import org.apache.hadoop.hbase.client.ConnectionFactory;
038import org.apache.hadoop.hbase.client.Put;
039import org.apache.hadoop.hbase.client.RegionInfo;
040import org.apache.hadoop.hbase.client.Table;
041import org.apache.hadoop.hbase.client.TableDescriptor;
042import org.apache.hadoop.hbase.master.RegionState;
043import org.apache.hadoop.hbase.master.ServerManager;
044import org.apache.hadoop.hbase.regionserver.HRegion;
045import org.apache.hadoop.hbase.security.User;
046import org.apache.yetus.audience.InterfaceAudience;
047import org.apache.zookeeper.KeeperException;
048import org.slf4j.Logger;
049import org.slf4j.LoggerFactory;
050
051/**
052 * This class contains helper methods that repair parts of hbase's filesystem contents.
053 */
054@InterfaceAudience.Private
055public class HBaseFsckRepair {
056  private static final Logger LOG = LoggerFactory.getLogger(HBaseFsckRepair.class);
057
058  /**
059   * Fix multiple assignment by doing silent closes on each RS hosting the region and then force ZK
060   * unassigned node to OFFLINE to trigger assignment by master.
061   * @param connection HBase connection to the cluster
062   * @param region     Region to undeploy
063   * @param servers    list of Servers to undeploy from
064   */
065  public static void fixMultiAssignment(Connection connection, RegionInfo region,
066    List<ServerName> servers) throws IOException, KeeperException, InterruptedException {
067    // Close region on the servers silently
068    for (ServerName server : servers) {
069      closeRegionSilentlyAndWait(connection, server, region);
070    }
071
072    // Force ZK node to OFFLINE so master assigns
073    forceOfflineInZK(connection.getAdmin(), region);
074  }
075
076  /**
077   * Fix unassigned by creating/transition the unassigned ZK node for this region to OFFLINE state
078   * with a special flag to tell the master that this is a forced operation by HBCK. This assumes
079   * that info is in META.
080   */
081  public static void fixUnassigned(Admin admin, RegionInfo region)
082    throws IOException, KeeperException, InterruptedException {
083    // Force ZK node to OFFLINE so master assigns
084    forceOfflineInZK(admin, region);
085  }
086
087  /**
088   * In 0.90, this forces an HRI offline by setting the RegionTransitionData in ZK to have
089   * HBCK_CODE_NAME as the server. This is a special case in the AssignmentManager that attempts an
090   * assign call by the master. This doesn't seem to work properly in the updated version of 0.92+'s
091   * hbck so we use assign to force the region into transition. This has the side-effect of
092   * requiring a RegionInfo that considers regionId (timestamp) in comparators that is addressed by
093   * HBASE-5563.
094   */
095  private static void forceOfflineInZK(Admin admin, final RegionInfo region)
096    throws ZooKeeperConnectionException, KeeperException, IOException, InterruptedException {
097    admin.assign(region.getRegionName());
098  }
099
100  /*
101   * Should we check all assignments or just not in RIT?
102   */
103  public static void waitUntilAssigned(Admin admin, RegionInfo region)
104    throws IOException, InterruptedException {
105    long timeout = admin.getConfiguration().getLong("hbase.hbck.assign.timeout", 120000);
106    long expiration = timeout + EnvironmentEdgeManager.currentTime();
107    while (EnvironmentEdgeManager.currentTime() < expiration) {
108      try {
109        boolean inTransition = false;
110        for (RegionState rs : admin.getClusterMetrics(EnumSet.of(Option.REGIONS_IN_TRANSITION))
111          .getRegionStatesInTransition()) {
112          if (RegionInfo.COMPARATOR.compare(rs.getRegion(), region) == 0) {
113            inTransition = true;
114            break;
115          }
116        }
117        if (!inTransition) {
118          // yay! no longer RIT
119          return;
120        }
121        // still in rit
122        LOG.info("Region still in transition, waiting for " + "it to become assigned: " + region);
123      } catch (IOException e) {
124        LOG.warn("Exception when waiting for region to become assigned," + " retrying", e);
125      }
126      Thread.sleep(1000);
127    }
128    throw new IOException("Region " + region + " failed to move out of "
129      + "transition within timeout " + timeout + "ms");
130  }
131
132  /**
133   * Contacts a region server and waits up to hbase.hbck.close.timeout ms (default 120s) to close
134   * the region. This bypasses the active hmaster.
135   */
136  public static void closeRegionSilentlyAndWait(Connection connection, ServerName server,
137    RegionInfo region) throws IOException, InterruptedException {
138    long timeout = connection.getConfiguration().getLong("hbase.hbck.close.timeout", 120000);
139    // this is a bit ugly but it is only used in the old hbck and tests, so I think it is fine.
140    try (AsyncClusterConnection asyncConn = ClusterConnectionFactory
141      .createAsyncClusterConnection(connection.getConfiguration(), null, User.getCurrent())) {
142      ServerManager.closeRegionSilentlyAndWait(asyncConn, server, region, timeout);
143    }
144  }
145
146  /**
147   * Puts the specified RegionInfo into META with replica related columns
148   */
149  public static void fixMetaHoleOnlineAndAddReplicas(Configuration conf, RegionInfo hri,
150    Collection<ServerName> servers, int numReplicas) throws IOException {
151    Connection conn = ConnectionFactory.createConnection(conf);
152    Table meta = conn.getTable(TableName.META_TABLE_NAME);
153    Put put = MetaTableAccessor.makePutFromRegionInfo(hri);
154    if (numReplicas > 1) {
155      Random rand = ThreadLocalRandom.current();
156      ServerName[] serversArr = servers.toArray(new ServerName[servers.size()]);
157      for (int i = 1; i < numReplicas; i++) {
158        ServerName sn = serversArr[rand.nextInt(serversArr.length)];
159        // the column added here is just to make sure the master is able to
160        // see the additional replicas when it is asked to assign. The
161        // final value of these columns will be different and will be updated
162        // by the actual regionservers that start hosting the respective replicas
163        MetaTableAccessor.addLocation(put, sn, sn.getStartcode(), i);
164      }
165    }
166    meta.put(put);
167    meta.close();
168    conn.close();
169  }
170
171  /**
172   * Creates, flushes, and closes a new region.
173   */
174  public static HRegion createHDFSRegionDir(Configuration conf, RegionInfo hri, TableDescriptor htd)
175    throws IOException {
176    // Create HRegion
177    Path root = CommonFSUtils.getRootDir(conf);
178    HRegion region = HRegion.createHRegion(hri, root, conf, htd, null);
179
180    // Close the new region to flush to disk. Close log file too.
181    region.close();
182    return region;
183  }
184
185  /*
186   * Remove parent
187   */
188  public static void removeParentInMeta(Configuration conf, RegionInfo hri) throws IOException {
189    throw new UnsupportedOperationException("HBCK1 is read-only now, use HBCK2 instead");
190  }
191}