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.Closeable;
021import java.io.FileNotFoundException;
022import java.io.IOException;
023import java.io.InterruptedIOException;
024import java.io.PrintWriter;
025import java.io.StringWriter;
026import java.net.InetAddress;
027import java.net.URI;
028import java.util.ArrayList;
029import java.util.Arrays;
030import java.util.Collection;
031import java.util.Collections;
032import java.util.Comparator;
033import java.util.EnumSet;
034import java.util.HashMap;
035import java.util.HashSet;
036import java.util.Iterator;
037import java.util.List;
038import java.util.Locale;
039import java.util.Map;
040import java.util.Map.Entry;
041import java.util.Objects;
042import java.util.Optional;
043import java.util.Set;
044import java.util.SortedMap;
045import java.util.SortedSet;
046import java.util.TreeMap;
047import java.util.TreeSet;
048import java.util.Vector;
049import java.util.concurrent.Callable;
050import java.util.concurrent.ConcurrentSkipListMap;
051import java.util.concurrent.ExecutionException;
052import java.util.concurrent.ExecutorService;
053import java.util.concurrent.Executors;
054import java.util.concurrent.Future;
055import java.util.concurrent.FutureTask;
056import java.util.concurrent.ScheduledThreadPoolExecutor;
057import java.util.concurrent.TimeUnit;
058import java.util.concurrent.TimeoutException;
059import java.util.concurrent.atomic.AtomicBoolean;
060import java.util.concurrent.atomic.AtomicInteger;
061import org.apache.commons.io.IOUtils;
062import org.apache.commons.lang3.RandomStringUtils;
063import org.apache.commons.lang3.StringUtils;
064import org.apache.hadoop.conf.Configuration;
065import org.apache.hadoop.conf.Configured;
066import org.apache.hadoop.fs.FSDataOutputStream;
067import org.apache.hadoop.fs.FileStatus;
068import org.apache.hadoop.fs.FileSystem;
069import org.apache.hadoop.fs.Path;
070import org.apache.hadoop.fs.permission.FsAction;
071import org.apache.hadoop.fs.permission.FsPermission;
072import org.apache.hadoop.hbase.Abortable;
073import org.apache.hadoop.hbase.Cell;
074import org.apache.hadoop.hbase.CellUtil;
075import org.apache.hadoop.hbase.ClusterMetrics;
076import org.apache.hadoop.hbase.ClusterMetrics.Option;
077import org.apache.hadoop.hbase.HBaseConfiguration;
078import org.apache.hadoop.hbase.HBaseInterfaceAudience;
079import org.apache.hadoop.hbase.HConstants;
080import org.apache.hadoop.hbase.HRegionInfo;
081import org.apache.hadoop.hbase.HRegionLocation;
082import org.apache.hadoop.hbase.KeyValue;
083import org.apache.hadoop.hbase.MasterNotRunningException;
084import org.apache.hadoop.hbase.MetaTableAccessor;
085import org.apache.hadoop.hbase.RegionLocations;
086import org.apache.hadoop.hbase.ServerName;
087import org.apache.hadoop.hbase.TableName;
088import org.apache.hadoop.hbase.ZooKeeperConnectionException;
089import org.apache.hadoop.hbase.client.Admin;
090import org.apache.hadoop.hbase.client.ClusterConnection;
091import org.apache.hadoop.hbase.client.ColumnFamilyDescriptor;
092import org.apache.hadoop.hbase.client.ColumnFamilyDescriptorBuilder;
093import org.apache.hadoop.hbase.client.Connection;
094import org.apache.hadoop.hbase.client.ConnectionFactory;
095import org.apache.hadoop.hbase.client.Delete;
096import org.apache.hadoop.hbase.client.Get;
097import org.apache.hadoop.hbase.client.Put;
098import org.apache.hadoop.hbase.client.RegionInfo;
099import org.apache.hadoop.hbase.client.RegionInfoBuilder;
100import org.apache.hadoop.hbase.client.RegionReplicaUtil;
101import org.apache.hadoop.hbase.client.Result;
102import org.apache.hadoop.hbase.client.RowMutations;
103import org.apache.hadoop.hbase.client.Table;
104import org.apache.hadoop.hbase.client.TableDescriptor;
105import org.apache.hadoop.hbase.client.TableDescriptorBuilder;
106import org.apache.hadoop.hbase.client.TableState;
107import org.apache.hadoop.hbase.io.FileLink;
108import org.apache.hadoop.hbase.io.HFileLink;
109import org.apache.hadoop.hbase.io.hfile.CacheConfig;
110import org.apache.hadoop.hbase.io.hfile.HFile;
111import org.apache.hadoop.hbase.log.HBaseMarkers;
112import org.apache.hadoop.hbase.master.MasterFileSystem;
113import org.apache.hadoop.hbase.master.RegionState;
114import org.apache.hadoop.hbase.regionserver.HRegion;
115import org.apache.hadoop.hbase.regionserver.HRegionFileSystem;
116import org.apache.hadoop.hbase.regionserver.StoreFileInfo;
117import org.apache.hadoop.hbase.security.AccessDeniedException;
118import org.apache.hadoop.hbase.security.UserProvider;
119import org.apache.hadoop.hbase.util.Bytes.ByteArrayComparator;
120import org.apache.hadoop.hbase.util.HBaseFsck.ErrorReporter.ERROR_CODE;
121import org.apache.hadoop.hbase.util.hbck.HFileCorruptionChecker;
122import org.apache.hadoop.hbase.util.hbck.ReplicationChecker;
123import org.apache.hadoop.hbase.util.hbck.TableIntegrityErrorHandler;
124import org.apache.hadoop.hbase.util.hbck.TableIntegrityErrorHandlerImpl;
125import org.apache.hadoop.hbase.wal.WAL;
126import org.apache.hadoop.hbase.wal.WALFactory;
127import org.apache.hadoop.hbase.wal.WALSplitter;
128import org.apache.hadoop.hbase.zookeeper.MetaTableLocator;
129import org.apache.hadoop.hbase.zookeeper.ZKUtil;
130import org.apache.hadoop.hbase.zookeeper.ZKWatcher;
131import org.apache.hadoop.hbase.zookeeper.ZNodePaths;
132import org.apache.hadoop.hdfs.protocol.AlreadyBeingCreatedException;
133import org.apache.hadoop.ipc.RemoteException;
134import org.apache.hadoop.security.UserGroupInformation;
135import org.apache.hadoop.util.ReflectionUtils;
136import org.apache.hadoop.util.Tool;
137import org.apache.hadoop.util.ToolRunner;
138import org.apache.hbase.thirdparty.com.google.common.annotations.VisibleForTesting;
139import org.apache.hbase.thirdparty.com.google.common.collect.Sets;
140import org.apache.yetus.audience.InterfaceAudience;
141import org.apache.yetus.audience.InterfaceStability;
142import org.apache.zookeeper.KeeperException;
143import org.slf4j.Logger;
144import org.slf4j.LoggerFactory;
145
146import org.apache.hbase.thirdparty.com.google.common.base.Joiner;
147import org.apache.hbase.thirdparty.com.google.common.base.Preconditions;
148import org.apache.hbase.thirdparty.com.google.common.collect.ImmutableList;
149import org.apache.hbase.thirdparty.com.google.common.collect.Lists;
150import org.apache.hbase.thirdparty.com.google.common.collect.Multimap;
151import org.apache.hbase.thirdparty.com.google.common.collect.Ordering;
152import org.apache.hbase.thirdparty.com.google.common.collect.TreeMultimap;
153
154import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil;
155import org.apache.hadoop.hbase.shaded.protobuf.generated.AdminProtos.AdminService.BlockingInterface;
156
157/**
158 * HBaseFsck (hbck) is a tool for checking and repairing region consistency and
159 * table integrity problems in a corrupted HBase. This tool was written for hbase-1.x. It does not
160 * work with hbase-2.x; it can read state but is not allowed to change state; i.e. effect 'repair'.
161 * See hbck2 (HBASE-19121) for a hbck tool for hbase2.
162 *
163 * <p>
164 * Region consistency checks verify that hbase:meta, region deployment on region
165 * servers and the state of data in HDFS (.regioninfo files) all are in
166 * accordance.
167 * <p>
168 * Table integrity checks verify that all possible row keys resolve to exactly
169 * one region of a table.  This means there are no individual degenerate
170 * or backwards regions; no holes between regions; and that there are no
171 * overlapping regions.
172 * <p>
173 * The general repair strategy works in two phases:
174 * <ol>
175 * <li> Repair Table Integrity on HDFS. (merge or fabricate regions)
176 * <li> Repair Region Consistency with hbase:meta and assignments
177 * </ol>
178 * <p>
179 * For table integrity repairs, the tables' region directories are scanned
180 * for .regioninfo files.  Each table's integrity is then verified.  If there
181 * are any orphan regions (regions with no .regioninfo files) or holes, new
182 * regions are fabricated.  Backwards regions are sidelined as well as empty
183 * degenerate (endkey==startkey) regions.  If there are any overlapping regions,
184 * a new region is created and all data is merged into the new region.
185 * <p>
186 * Table integrity repairs deal solely with HDFS and could potentially be done
187 * offline -- the hbase region servers or master do not need to be running.
188 * This phase can eventually be used to completely reconstruct the hbase:meta table in
189 * an offline fashion.
190 * <p>
191 * Region consistency requires three conditions -- 1) valid .regioninfo file
192 * present in an HDFS region dir,  2) valid row with .regioninfo data in META,
193 * and 3) a region is deployed only at the regionserver that was assigned to
194 * with proper state in the master.
195 * <p>
196 * Region consistency repairs require hbase to be online so that hbck can
197 * contact the HBase master and region servers.  The hbck#connect() method must
198 * first be called successfully.  Much of the region consistency information
199 * is transient and less risky to repair.
200 * <p>
201 * If hbck is run from the command line, there are a handful of arguments that
202 * can be used to limit the kinds of repairs hbck will do.  See the code in
203 * {@link #printUsageAndExit()} for more details.
204 */
205@InterfaceAudience.LimitedPrivate(HBaseInterfaceAudience.TOOLS)
206@InterfaceStability.Evolving
207public class HBaseFsck extends Configured implements Closeable {
208  public static final long DEFAULT_TIME_LAG = 60000; // default value of 1 minute
209  public static final long DEFAULT_SLEEP_BEFORE_RERUN = 10000;
210  private static final int MAX_NUM_THREADS = 50; // #threads to contact regions
211  private static boolean rsSupportsOffline = true;
212  private static final int DEFAULT_OVERLAPS_TO_SIDELINE = 2;
213  private static final int DEFAULT_MAX_MERGE = 5;
214  private static final String TO_BE_LOADED = "to_be_loaded";
215  /**
216   * Here is where hbase-1.x used to default the lock for hbck1.
217   * It puts in place a lock when it goes to write/make changes.
218   */
219  @VisibleForTesting
220  public static final String HBCK_LOCK_FILE = "hbase-hbck.lock";
221  private static final int DEFAULT_MAX_LOCK_FILE_ATTEMPTS = 5;
222  private static final int DEFAULT_LOCK_FILE_ATTEMPT_SLEEP_INTERVAL = 200; // milliseconds
223  private static final int DEFAULT_LOCK_FILE_ATTEMPT_MAX_SLEEP_TIME = 5000; // milliseconds
224  // We have to set the timeout value > HdfsConstants.LEASE_SOFTLIMIT_PERIOD.
225  // In HADOOP-2.6 and later, the Namenode proxy now created with custom RetryPolicy for
226  // AlreadyBeingCreatedException which is implies timeout on this operations up to
227  // HdfsConstants.LEASE_SOFTLIMIT_PERIOD (60 seconds).
228  private static final int DEFAULT_WAIT_FOR_LOCK_TIMEOUT = 80; // seconds
229  private static final int DEFAULT_MAX_CREATE_ZNODE_ATTEMPTS = 5;
230  private static final int DEFAULT_CREATE_ZNODE_ATTEMPT_SLEEP_INTERVAL = 200; // milliseconds
231  private static final int DEFAULT_CREATE_ZNODE_ATTEMPT_MAX_SLEEP_TIME = 5000; // milliseconds
232
233  /**********************
234   * Internal resources
235   **********************/
236  private static final Logger LOG = LoggerFactory.getLogger(HBaseFsck.class.getName());
237  private ClusterMetrics status;
238  private ClusterConnection connection;
239  private Admin admin;
240  private Table meta;
241  // threads to do ||izable tasks: retrieve data from regionservers, handle overlapping regions
242  protected ExecutorService executor;
243  private long startMillis = EnvironmentEdgeManager.currentTime();
244  private HFileCorruptionChecker hfcc;
245  private int retcode = 0;
246  private Path HBCK_LOCK_PATH;
247  private FSDataOutputStream hbckOutFd;
248  // This lock is to prevent cleanup of balancer resources twice between
249  // ShutdownHook and the main code. We cleanup only if the connect() is
250  // successful
251  private final AtomicBoolean hbckLockCleanup = new AtomicBoolean(false);
252
253  // Unsupported options in HBase 2.0+
254  private static final Set<String> unsupportedOptionsInV2 = Sets.newHashSet("-fix",
255      "-fixAssignments", "-fixMeta", "-fixHdfsHoles", "-fixHdfsOrphans", "-fixTableOrphans",
256      "-fixHdfsOverlaps", "-sidelineBigOverlaps", "-fixSplitParents", "-removeParents",
257      "-fixEmptyMetaCells", "-repair", "-repairHoles", "-maxOverlapsToSideline", "-maxMerge");
258
259  /***********
260   * Options
261   ***********/
262  private static boolean details = false; // do we display the full report
263  private long timelag = DEFAULT_TIME_LAG; // tables whose modtime is older
264  private static boolean forceExclusive = false; // only this hbck can modify HBase
265  private boolean fixAssignments = false; // fix assignment errors?
266  private boolean fixMeta = false; // fix meta errors?
267  private boolean checkHdfs = true; // load and check fs consistency?
268  private boolean fixHdfsHoles = false; // fix fs holes?
269  private boolean fixHdfsOverlaps = false; // fix fs overlaps (risky)
270  private boolean fixHdfsOrphans = false; // fix fs holes (missing .regioninfo)
271  private boolean fixTableOrphans = false; // fix fs holes (missing .tableinfo)
272  private boolean fixVersionFile = false; // fix missing hbase.version file in hdfs
273  private boolean fixSplitParents = false; // fix lingering split parents
274  private boolean removeParents = false; // remove split parents
275  private boolean fixReferenceFiles = false; // fix lingering reference store file
276  private boolean fixHFileLinks = false; // fix lingering HFileLinks
277  private boolean fixEmptyMetaCells = false; // fix (remove) empty REGIONINFO_QUALIFIER rows
278  private boolean fixReplication = false; // fix undeleted replication queues for removed peer
279  private boolean fixAny = false; // Set to true if any of the fix is required.
280
281  // limit checking/fixes to listed tables, if empty attempt to check/fix all
282  // hbase:meta are always checked
283  private Set<TableName> tablesIncluded = new HashSet<>();
284  private int maxMerge = DEFAULT_MAX_MERGE; // maximum number of overlapping regions to merge
285  // maximum number of overlapping regions to sideline
286  private int maxOverlapsToSideline = DEFAULT_OVERLAPS_TO_SIDELINE;
287  private boolean sidelineBigOverlaps = false; // sideline overlaps with >maxMerge regions
288  private Path sidelineDir = null;
289
290  private boolean rerun = false; // if we tried to fix something, rerun hbck
291  private static boolean summary = false; // if we want to print less output
292  private boolean checkMetaOnly = false;
293  private boolean checkRegionBoundaries = false;
294  private boolean ignorePreCheckPermission = false; // if pre-check permission
295
296  /*********
297   * State
298   *********/
299  final private ErrorReporter errors;
300  int fixes = 0;
301
302  /**
303   * This map contains the state of all hbck items.  It maps from encoded region
304   * name to HbckInfo structure.  The information contained in HbckInfo is used
305   * to detect and correct consistency (hdfs/meta/deployment) problems.
306   */
307  private TreeMap<String, HbckInfo> regionInfoMap = new TreeMap<>();
308  // Empty regioninfo qualifiers in hbase:meta
309  private Set<Result> emptyRegionInfoQualifiers = new HashSet<>();
310
311  /**
312   * This map from Tablename -> TableInfo contains the structures necessary to
313   * detect table consistency problems (holes, dupes, overlaps).  It is sorted
314   * to prevent dupes.
315   *
316   * If tablesIncluded is empty, this map contains all tables.
317   * Otherwise, it contains only meta tables and tables in tablesIncluded,
318   * unless checkMetaOnly is specified, in which case, it contains only
319   * the meta table
320   */
321  private SortedMap<TableName, TableInfo> tablesInfo = new ConcurrentSkipListMap<>();
322
323  /**
324   * When initially looking at HDFS, we attempt to find any orphaned data.
325   */
326  private List<HbckInfo> orphanHdfsDirs = Collections.synchronizedList(new ArrayList<HbckInfo>());
327
328  private Map<TableName, Set<String>> orphanTableDirs = new HashMap<>();
329  private Map<TableName, TableState> tableStates = new HashMap<>();
330  private final RetryCounterFactory lockFileRetryCounterFactory;
331  private final RetryCounterFactory createZNodeRetryCounterFactory;
332
333  private Map<TableName, Set<String>> skippedRegions = new HashMap<>();
334
335  private ZKWatcher zkw = null;
336  private String hbckEphemeralNodePath = null;
337  private boolean hbckZodeCreated = false;
338
339  /**
340   * Constructor
341   *
342   * @param conf Configuration object
343   * @throws MasterNotRunningException if the master is not running
344   * @throws ZooKeeperConnectionException if unable to connect to ZooKeeper
345   */
346  public HBaseFsck(Configuration conf) throws MasterNotRunningException,
347      ZooKeeperConnectionException, IOException, ClassNotFoundException {
348    this(conf, createThreadPool(conf));
349  }
350
351  private static ExecutorService createThreadPool(Configuration conf) {
352    int numThreads = conf.getInt("hbasefsck.numthreads", MAX_NUM_THREADS);
353    return new ScheduledThreadPoolExecutor(numThreads, Threads.newDaemonThreadFactory("hbasefsck"));
354  }
355
356  /**
357   * Constructor
358   *
359   * @param conf
360   *          Configuration object
361   * @throws MasterNotRunningException
362   *           if the master is not running
363   * @throws ZooKeeperConnectionException
364   *           if unable to connect to ZooKeeper
365   */
366  public HBaseFsck(Configuration conf, ExecutorService exec) throws MasterNotRunningException,
367      ZooKeeperConnectionException, IOException, ClassNotFoundException {
368    super(conf);
369    errors = getErrorReporter(getConf());
370    this.executor = exec;
371    lockFileRetryCounterFactory = createLockRetryCounterFactory(getConf());
372    createZNodeRetryCounterFactory = createZnodeRetryCounterFactory(getConf());
373    zkw = createZooKeeperWatcher();
374  }
375
376  /**
377   * @return A retry counter factory configured for retrying lock file creation.
378   */
379  public static RetryCounterFactory createLockRetryCounterFactory(Configuration conf) {
380    return new RetryCounterFactory(
381        conf.getInt("hbase.hbck.lockfile.attempts", DEFAULT_MAX_LOCK_FILE_ATTEMPTS),
382        conf.getInt("hbase.hbck.lockfile.attempt.sleep.interval",
383            DEFAULT_LOCK_FILE_ATTEMPT_SLEEP_INTERVAL),
384        conf.getInt("hbase.hbck.lockfile.attempt.maxsleeptime",
385            DEFAULT_LOCK_FILE_ATTEMPT_MAX_SLEEP_TIME));
386  }
387
388  /**
389   * @return A retry counter factory configured for retrying znode creation.
390   */
391  private static RetryCounterFactory createZnodeRetryCounterFactory(Configuration conf) {
392    return new RetryCounterFactory(
393        conf.getInt("hbase.hbck.createznode.attempts", DEFAULT_MAX_CREATE_ZNODE_ATTEMPTS),
394        conf.getInt("hbase.hbck.createznode.attempt.sleep.interval",
395            DEFAULT_CREATE_ZNODE_ATTEMPT_SLEEP_INTERVAL),
396        conf.getInt("hbase.hbck.createznode.attempt.maxsleeptime",
397            DEFAULT_CREATE_ZNODE_ATTEMPT_MAX_SLEEP_TIME));
398  }
399
400  /**
401   * @return Return the tmp dir this tool writes too.
402   */
403  @VisibleForTesting
404  public static Path getTmpDir(Configuration conf) throws IOException {
405    return new Path(FSUtils.getRootDir(conf), HConstants.HBASE_TEMP_DIRECTORY);
406  }
407
408  private static class FileLockCallable implements Callable<FSDataOutputStream> {
409    RetryCounter retryCounter;
410    private final Configuration conf;
411    private Path hbckLockPath = null;
412
413    public FileLockCallable(Configuration conf, RetryCounter retryCounter) {
414      this.retryCounter = retryCounter;
415      this.conf = conf;
416    }
417
418    /**
419     * @return Will be <code>null</code> unless you call {@link #call()}
420     */
421    Path getHbckLockPath() {
422      return this.hbckLockPath;
423    }
424
425    @Override
426    public FSDataOutputStream call() throws IOException {
427      try {
428        FileSystem fs = FSUtils.getCurrentFileSystem(this.conf);
429        FsPermission defaultPerms = FSUtils.getFilePermissions(fs, this.conf,
430            HConstants.DATA_FILE_UMASK_KEY);
431        Path tmpDir = getTmpDir(conf);
432        this.hbckLockPath = new Path(tmpDir, HBCK_LOCK_FILE);
433        fs.mkdirs(tmpDir);
434        final FSDataOutputStream out = createFileWithRetries(fs, this.hbckLockPath, defaultPerms);
435        out.writeBytes(InetAddress.getLocalHost().toString());
436        // Add a note into the file we write on why hbase2 is writing out an hbck1 lock file.
437        out.writeBytes(" Written by an hbase-2.x Master to block an " +
438            "attempt by an hbase-1.x HBCK tool making modification to state. " +
439            "See 'HBCK must match HBase server version' in the hbase refguide.");
440        out.flush();
441        return out;
442      } catch(RemoteException e) {
443        if(AlreadyBeingCreatedException.class.getName().equals(e.getClassName())){
444          return null;
445        } else {
446          throw e;
447        }
448      }
449    }
450
451    private FSDataOutputStream createFileWithRetries(final FileSystem fs,
452        final Path hbckLockFilePath, final FsPermission defaultPerms)
453        throws IOException {
454      IOException exception = null;
455      do {
456        try {
457          return FSUtils.create(fs, hbckLockFilePath, defaultPerms, false);
458        } catch (IOException ioe) {
459          LOG.info("Failed to create lock file " + hbckLockFilePath.getName()
460              + ", try=" + (retryCounter.getAttemptTimes() + 1) + " of "
461              + retryCounter.getMaxAttempts());
462          LOG.debug("Failed to create lock file " + hbckLockFilePath.getName(),
463              ioe);
464          try {
465            exception = ioe;
466            retryCounter.sleepUntilNextRetry();
467          } catch (InterruptedException ie) {
468            throw (InterruptedIOException) new InterruptedIOException(
469                "Can't create lock file " + hbckLockFilePath.getName())
470            .initCause(ie);
471          }
472        }
473      } while (retryCounter.shouldRetry());
474
475      throw exception;
476    }
477  }
478
479  /**
480   * This method maintains a lock using a file. If the creation fails we return null
481   *
482   * @return FSDataOutputStream object corresponding to the newly opened lock file
483   * @throws IOException if IO failure occurs
484   */
485  public static Pair<Path, FSDataOutputStream> checkAndMarkRunningHbck(Configuration conf,
486      RetryCounter retryCounter) throws IOException {
487    FileLockCallable callable = new FileLockCallable(conf, retryCounter);
488    ExecutorService executor = Executors.newFixedThreadPool(1);
489    FutureTask<FSDataOutputStream> futureTask = new FutureTask<>(callable);
490    executor.execute(futureTask);
491    final int timeoutInSeconds = conf.getInt(
492      "hbase.hbck.lockfile.maxwaittime", DEFAULT_WAIT_FOR_LOCK_TIMEOUT);
493    FSDataOutputStream stream = null;
494    try {
495      stream = futureTask.get(timeoutInSeconds, TimeUnit.SECONDS);
496    } catch (ExecutionException ee) {
497      LOG.warn("Encountered exception when opening lock file", ee);
498    } catch (InterruptedException ie) {
499      LOG.warn("Interrupted when opening lock file", ie);
500      Thread.currentThread().interrupt();
501    } catch (TimeoutException exception) {
502      // took too long to obtain lock
503      LOG.warn("Took more than " + timeoutInSeconds + " seconds in obtaining lock");
504      futureTask.cancel(true);
505    } finally {
506      executor.shutdownNow();
507    }
508    return new Pair<Path, FSDataOutputStream>(callable.getHbckLockPath(), stream);
509  }
510
511  private void unlockHbck() {
512    if (isExclusive() && hbckLockCleanup.compareAndSet(true, false)) {
513      RetryCounter retryCounter = lockFileRetryCounterFactory.create();
514      do {
515        try {
516          IOUtils.closeQuietly(hbckOutFd);
517          FSUtils.delete(FSUtils.getCurrentFileSystem(getConf()), HBCK_LOCK_PATH, true);
518          LOG.info("Finishing hbck");
519          return;
520        } catch (IOException ioe) {
521          LOG.info("Failed to delete " + HBCK_LOCK_PATH + ", try="
522              + (retryCounter.getAttemptTimes() + 1) + " of "
523              + retryCounter.getMaxAttempts());
524          LOG.debug("Failed to delete " + HBCK_LOCK_PATH, ioe);
525          try {
526            retryCounter.sleepUntilNextRetry();
527          } catch (InterruptedException ie) {
528            Thread.currentThread().interrupt();
529            LOG.warn("Interrupted while deleting lock file" +
530                HBCK_LOCK_PATH);
531            return;
532          }
533        }
534      } while (retryCounter.shouldRetry());
535    }
536  }
537
538  /**
539   * To repair region consistency, one must call connect() in order to repair
540   * online state.
541   */
542  public void connect() throws IOException {
543
544    if (isExclusive()) {
545      // Grab the lock
546      Pair<Path, FSDataOutputStream> pair =
547          checkAndMarkRunningHbck(getConf(), this.lockFileRetryCounterFactory.create());
548      HBCK_LOCK_PATH = pair.getFirst();
549      this.hbckOutFd = pair.getSecond();
550      if (hbckOutFd == null) {
551        setRetCode(-1);
552        LOG.error("Another instance of hbck is fixing HBase, exiting this instance. " +
553            "[If you are sure no other instance is running, delete the lock file " +
554            HBCK_LOCK_PATH + " and rerun the tool]");
555        throw new IOException("Duplicate hbck - Abort");
556      }
557
558      // Make sure to cleanup the lock
559      hbckLockCleanup.set(true);
560    }
561
562
563    // Add a shutdown hook to this thread, in case user tries to
564    // kill the hbck with a ctrl-c, we want to cleanup the lock so that
565    // it is available for further calls
566    Runtime.getRuntime().addShutdownHook(new Thread() {
567      @Override
568      public void run() {
569        IOUtils.closeQuietly(HBaseFsck.this);
570        cleanupHbckZnode();
571        unlockHbck();
572      }
573    });
574
575    LOG.info("Launching hbck");
576
577    connection = (ClusterConnection)ConnectionFactory.createConnection(getConf());
578    admin = connection.getAdmin();
579    meta = connection.getTable(TableName.META_TABLE_NAME);
580    status = admin.getClusterMetrics(EnumSet.of(Option.LIVE_SERVERS,
581      Option.DEAD_SERVERS, Option.MASTER, Option.BACKUP_MASTERS,
582      Option.REGIONS_IN_TRANSITION, Option.HBASE_VERSION));
583  }
584
585  /**
586   * Get deployed regions according to the region servers.
587   */
588  private void loadDeployedRegions() throws IOException, InterruptedException {
589    // From the master, get a list of all known live region servers
590    Collection<ServerName> regionServers = status.getLiveServerMetrics().keySet();
591    errors.print("Number of live region servers: " + regionServers.size());
592    if (details) {
593      for (ServerName rsinfo: regionServers) {
594        errors.print("  " + rsinfo.getServerName());
595      }
596    }
597
598    // From the master, get a list of all dead region servers
599    Collection<ServerName> deadRegionServers = status.getDeadServerNames();
600    errors.print("Number of dead region servers: " + deadRegionServers.size());
601    if (details) {
602      for (ServerName name: deadRegionServers) {
603        errors.print("  " + name);
604      }
605    }
606
607    // Print the current master name and state
608    errors.print("Master: " + status.getMasterName());
609
610    // Print the list of all backup masters
611    Collection<ServerName> backupMasters = status.getBackupMasterNames();
612    errors.print("Number of backup masters: " + backupMasters.size());
613    if (details) {
614      for (ServerName name: backupMasters) {
615        errors.print("  " + name);
616      }
617    }
618
619    errors.print("Average load: " + status.getAverageLoad());
620    errors.print("Number of requests: " + status.getRequestCount());
621    errors.print("Number of regions: " + status.getRegionCount());
622
623    List<RegionState> rits = status.getRegionStatesInTransition();
624    errors.print("Number of regions in transition: " + rits.size());
625    if (details) {
626      for (RegionState state: rits) {
627        errors.print("  " + state.toDescriptiveString());
628      }
629    }
630
631    // Determine what's deployed
632    processRegionServers(regionServers);
633  }
634
635  /**
636   * Clear the current state of hbck.
637   */
638  private void clearState() {
639    // Make sure regionInfo is empty before starting
640    fixes = 0;
641    regionInfoMap.clear();
642    emptyRegionInfoQualifiers.clear();
643    tableStates.clear();
644    errors.clear();
645    tablesInfo.clear();
646    orphanHdfsDirs.clear();
647    skippedRegions.clear();
648  }
649
650  /**
651   * This repair method analyzes hbase data in hdfs and repairs it to satisfy
652   * the table integrity rules.  HBase doesn't need to be online for this
653   * operation to work.
654   */
655  public void offlineHdfsIntegrityRepair() throws IOException, InterruptedException {
656    // Initial pass to fix orphans.
657    if (shouldCheckHdfs() && (shouldFixHdfsOrphans() || shouldFixHdfsHoles()
658        || shouldFixHdfsOverlaps() || shouldFixTableOrphans())) {
659      LOG.info("Loading regioninfos HDFS");
660      // if nothing is happening this should always complete in two iterations.
661      int maxIterations = getConf().getInt("hbase.hbck.integrityrepair.iterations.max", 3);
662      int curIter = 0;
663      do {
664        clearState(); // clears hbck state and reset fixes to 0 and.
665        // repair what's on HDFS
666        restoreHdfsIntegrity();
667        curIter++;// limit the number of iterations.
668      } while (fixes > 0 && curIter <= maxIterations);
669
670      // Repairs should be done in the first iteration and verification in the second.
671      // If there are more than 2 passes, something funny has happened.
672      if (curIter > 2) {
673        if (curIter == maxIterations) {
674          LOG.warn("Exiting integrity repairs after max " + curIter + " iterations. "
675              + "Tables integrity may not be fully repaired!");
676        } else {
677          LOG.info("Successfully exiting integrity repairs after " + curIter + " iterations");
678        }
679      }
680    }
681  }
682
683  /**
684   * This repair method requires the cluster to be online since it contacts
685   * region servers and the masters.  It makes each region's state in HDFS, in
686   * hbase:meta, and deployments consistent.
687   *
688   * @return If &gt; 0 , number of errors detected, if &lt; 0 there was an unrecoverable
689   *     error.  If 0, we have a clean hbase.
690   */
691  public int onlineConsistencyRepair() throws IOException, KeeperException,
692    InterruptedException {
693
694    // get regions according to what is online on each RegionServer
695    loadDeployedRegions();
696    // check whether hbase:meta is deployed and online
697    recordMetaRegion();
698    // Check if hbase:meta is found only once and in the right place
699    if (!checkMetaRegion()) {
700      String errorMsg = "hbase:meta table is not consistent. ";
701      if (shouldFixAssignments()) {
702        errorMsg += "HBCK will try fixing it. Rerun once hbase:meta is back to consistent state.";
703      } else {
704        errorMsg += "Run HBCK with proper fix options to fix hbase:meta inconsistency.";
705      }
706      errors.reportError(errorMsg + " Exiting...");
707      return -2;
708    }
709    // Not going with further consistency check for tables when hbase:meta itself is not consistent.
710    LOG.info("Loading regionsinfo from the hbase:meta table");
711    boolean success = loadMetaEntries();
712    if (!success) return -1;
713
714    // Empty cells in hbase:meta?
715    reportEmptyMetaCells();
716
717    // Check if we have to cleanup empty REGIONINFO_QUALIFIER rows from hbase:meta
718    if (shouldFixEmptyMetaCells()) {
719      fixEmptyMetaCells();
720    }
721
722    // get a list of all tables that have not changed recently.
723    if (!checkMetaOnly) {
724      reportTablesInFlux();
725    }
726
727    // Get disabled tables states
728    loadTableStates();
729
730    // load regiondirs and regioninfos from HDFS
731    if (shouldCheckHdfs()) {
732      LOG.info("Loading region directories from HDFS");
733      loadHdfsRegionDirs();
734      LOG.info("Loading region information from HDFS");
735      loadHdfsRegionInfos();
736    }
737
738    // fix the orphan tables
739    fixOrphanTables();
740
741    LOG.info("Checking and fixing region consistency");
742    // Check and fix consistency
743    checkAndFixConsistency();
744
745    // Check integrity (does not fix)
746    checkIntegrity();
747    return errors.getErrorList().size();
748  }
749
750  /**
751   * This method maintains an ephemeral znode. If the creation fails we return false or throw
752   * exception
753   *
754   * @return true if creating znode succeeds; false otherwise
755   * @throws IOException if IO failure occurs
756   */
757  private boolean setMasterInMaintenanceMode() throws IOException {
758    RetryCounter retryCounter = createZNodeRetryCounterFactory.create();
759    hbckEphemeralNodePath = ZNodePaths.joinZNode(
760      zkw.znodePaths.masterMaintZNode,
761      "hbck-" + Long.toString(EnvironmentEdgeManager.currentTime()));
762    do {
763      try {
764        hbckZodeCreated = ZKUtil.createEphemeralNodeAndWatch(zkw, hbckEphemeralNodePath, null);
765        if (hbckZodeCreated) {
766          break;
767        }
768      } catch (KeeperException e) {
769        if (retryCounter.getAttemptTimes() >= retryCounter.getMaxAttempts()) {
770           throw new IOException("Can't create znode " + hbckEphemeralNodePath, e);
771        }
772        // fall through and retry
773      }
774
775      LOG.warn("Fail to create znode " + hbckEphemeralNodePath + ", try=" +
776          (retryCounter.getAttemptTimes() + 1) + " of " + retryCounter.getMaxAttempts());
777
778      try {
779        retryCounter.sleepUntilNextRetry();
780      } catch (InterruptedException ie) {
781        throw (InterruptedIOException) new InterruptedIOException(
782              "Can't create znode " + hbckEphemeralNodePath).initCause(ie);
783      }
784    } while (retryCounter.shouldRetry());
785    return hbckZodeCreated;
786  }
787
788  private void cleanupHbckZnode() {
789    try {
790      if (zkw != null && hbckZodeCreated) {
791        ZKUtil.deleteNode(zkw, hbckEphemeralNodePath);
792        hbckZodeCreated = false;
793      }
794    } catch (KeeperException e) {
795      // Ignore
796      if (!e.code().equals(KeeperException.Code.NONODE)) {
797        LOG.warn("Delete HBCK znode " + hbckEphemeralNodePath + " failed ", e);
798      }
799    }
800  }
801
802  /**
803   * Contacts the master and prints out cluster-wide information
804   * @return 0 on success, non-zero on failure
805   */
806  public int onlineHbck()
807      throws IOException, KeeperException, InterruptedException {
808    // print hbase server version
809    errors.print("Version: " + status.getHBaseVersion());
810
811    // Clean start
812    clearState();
813    // Do offline check and repair first
814    offlineHdfsIntegrityRepair();
815    offlineReferenceFileRepair();
816    offlineHLinkFileRepair();
817    // If Master runs maintenance tasks (such as balancer, catalog janitor, etc) during online
818    // hbck, it is likely that hbck would be misled and report transient errors.  Therefore, it
819    // is better to set Master into maintenance mode during online hbck.
820    //
821    if (!setMasterInMaintenanceMode()) {
822      LOG.warn("HBCK is running while master is not in maintenance mode, you might see transient "
823        + "error.  Please run HBCK multiple times to reduce the chance of transient error.");
824    }
825
826    onlineConsistencyRepair();
827
828    if (checkRegionBoundaries) {
829      checkRegionBoundaries();
830    }
831
832    checkAndFixReplication();
833
834    // Remove the hbck znode
835    cleanupHbckZnode();
836
837    // Remove the hbck lock
838    unlockHbck();
839
840    // Print table summary
841    printTableSummary(tablesInfo);
842    return errors.summarize();
843  }
844
845  public static byte[] keyOnly (byte[] b) {
846    if (b == null)
847      return b;
848    int rowlength = Bytes.toShort(b, 0);
849    byte[] result = new byte[rowlength];
850    System.arraycopy(b, Bytes.SIZEOF_SHORT, result, 0, rowlength);
851    return result;
852  }
853
854  @Override
855  public void close() throws IOException {
856    try {
857      cleanupHbckZnode();
858      unlockHbck();
859    } catch (Exception io) {
860      LOG.warn(io.toString(), io);
861    } finally {
862      if (zkw != null) {
863        zkw.close();
864        zkw = null;
865      }
866      IOUtils.closeQuietly(admin);
867      IOUtils.closeQuietly(meta);
868      IOUtils.closeQuietly(connection);
869    }
870  }
871
872  private static class RegionBoundariesInformation {
873    public byte [] regionName;
874    public byte [] metaFirstKey;
875    public byte [] metaLastKey;
876    public byte [] storesFirstKey;
877    public byte [] storesLastKey;
878    @Override
879    public String toString () {
880      return "regionName=" + Bytes.toStringBinary(regionName) +
881             "\nmetaFirstKey=" + Bytes.toStringBinary(metaFirstKey) +
882             "\nmetaLastKey=" + Bytes.toStringBinary(metaLastKey) +
883             "\nstoresFirstKey=" + Bytes.toStringBinary(storesFirstKey) +
884             "\nstoresLastKey=" + Bytes.toStringBinary(storesLastKey);
885    }
886  }
887
888  public void checkRegionBoundaries() {
889    try {
890      ByteArrayComparator comparator = new ByteArrayComparator();
891      List<RegionInfo> regions = MetaTableAccessor.getAllRegions(connection, true);
892      final RegionBoundariesInformation currentRegionBoundariesInformation =
893          new RegionBoundariesInformation();
894      Path hbaseRoot = FSUtils.getRootDir(getConf());
895      for (RegionInfo regionInfo : regions) {
896        Path tableDir = FSUtils.getTableDir(hbaseRoot, regionInfo.getTable());
897        currentRegionBoundariesInformation.regionName = regionInfo.getRegionName();
898        // For each region, get the start and stop key from the META and compare them to the
899        // same information from the Stores.
900        Path path = new Path(tableDir, regionInfo.getEncodedName());
901        FileSystem fs = path.getFileSystem(getConf());
902        FileStatus[] files = fs.listStatus(path);
903        // For all the column families in this region...
904        byte[] storeFirstKey = null;
905        byte[] storeLastKey = null;
906        for (FileStatus file : files) {
907          String fileName = file.getPath().toString();
908          fileName = fileName.substring(fileName.lastIndexOf("/") + 1);
909          if (!fileName.startsWith(".") && !fileName.endsWith("recovered.edits")) {
910            FileStatus[] storeFiles = fs.listStatus(file.getPath());
911            // For all the stores in this column family.
912            for (FileStatus storeFile : storeFiles) {
913              HFile.Reader reader = HFile.createReader(fs, storeFile.getPath(),
914                CacheConfig.DISABLED, true, getConf());
915              if ((reader.getFirstKey() != null)
916                  && ((storeFirstKey == null) || (comparator.compare(storeFirstKey,
917                      ((KeyValue.KeyOnlyKeyValue) reader.getFirstKey().get()).getKey()) > 0))) {
918                storeFirstKey = ((KeyValue.KeyOnlyKeyValue)reader.getFirstKey().get()).getKey();
919              }
920              if ((reader.getLastKey() != null)
921                  && ((storeLastKey == null) || (comparator.compare(storeLastKey,
922                      ((KeyValue.KeyOnlyKeyValue)reader.getLastKey().get()).getKey())) < 0)) {
923                storeLastKey = ((KeyValue.KeyOnlyKeyValue)reader.getLastKey().get()).getKey();
924              }
925              reader.close();
926            }
927          }
928        }
929        currentRegionBoundariesInformation.metaFirstKey = regionInfo.getStartKey();
930        currentRegionBoundariesInformation.metaLastKey = regionInfo.getEndKey();
931        currentRegionBoundariesInformation.storesFirstKey = keyOnly(storeFirstKey);
932        currentRegionBoundariesInformation.storesLastKey = keyOnly(storeLastKey);
933        if (currentRegionBoundariesInformation.metaFirstKey.length == 0)
934          currentRegionBoundariesInformation.metaFirstKey = null;
935        if (currentRegionBoundariesInformation.metaLastKey.length == 0)
936          currentRegionBoundariesInformation.metaLastKey = null;
937
938        // For a region to be correct, we need the META start key to be smaller or equal to the
939        // smallest start key from all the stores, and the start key from the next META entry to
940        // be bigger than the last key from all the current stores. First region start key is null;
941        // Last region end key is null; some regions can be empty and not have any store.
942
943        boolean valid = true;
944        // Checking start key.
945        if ((currentRegionBoundariesInformation.storesFirstKey != null)
946            && (currentRegionBoundariesInformation.metaFirstKey != null)) {
947          valid = valid
948              && comparator.compare(currentRegionBoundariesInformation.storesFirstKey,
949                currentRegionBoundariesInformation.metaFirstKey) >= 0;
950        }
951        // Checking stop key.
952        if ((currentRegionBoundariesInformation.storesLastKey != null)
953            && (currentRegionBoundariesInformation.metaLastKey != null)) {
954          valid = valid
955              && comparator.compare(currentRegionBoundariesInformation.storesLastKey,
956                currentRegionBoundariesInformation.metaLastKey) < 0;
957        }
958        if (!valid) {
959          errors.reportError(ERROR_CODE.BOUNDARIES_ERROR, "Found issues with regions boundaries",
960            tablesInfo.get(regionInfo.getTable()));
961          LOG.warn("Region's boundaries not aligned between stores and META for:");
962          LOG.warn(Objects.toString(currentRegionBoundariesInformation));
963        }
964      }
965    } catch (IOException e) {
966      LOG.error(e.toString(), e);
967    }
968  }
969
970  /**
971   * Iterates through the list of all orphan/invalid regiondirs.
972   */
973  private void adoptHdfsOrphans(Collection<HbckInfo> orphanHdfsDirs) throws IOException {
974    for (HbckInfo hi : orphanHdfsDirs) {
975      LOG.info("Attempting to handle orphan hdfs dir: " + hi.getHdfsRegionDir());
976      adoptHdfsOrphan(hi);
977    }
978  }
979
980  /**
981   * Orphaned regions are regions without a .regioninfo file in them.  We "adopt"
982   * these orphans by creating a new region, and moving the column families,
983   * recovered edits, WALs, into the new region dir.  We determine the region
984   * startkey and endkeys by looking at all of the hfiles inside the column
985   * families to identify the min and max keys. The resulting region will
986   * likely violate table integrity but will be dealt with by merging
987   * overlapping regions.
988   */
989  @SuppressWarnings("deprecation")
990  private void adoptHdfsOrphan(HbckInfo hi) throws IOException {
991    Path p = hi.getHdfsRegionDir();
992    FileSystem fs = p.getFileSystem(getConf());
993    FileStatus[] dirs = fs.listStatus(p);
994    if (dirs == null) {
995      LOG.warn("Attempt to adopt orphan hdfs region skipped because no files present in " +
996          p + ". This dir could probably be deleted.");
997      return ;
998    }
999
1000    TableName tableName = hi.getTableName();
1001    TableInfo tableInfo = tablesInfo.get(tableName);
1002    Preconditions.checkNotNull(tableInfo, "Table '" + tableName + "' not present!");
1003    TableDescriptor template = tableInfo.getHTD();
1004
1005    // find min and max key values
1006    Pair<byte[],byte[]> orphanRegionRange = null;
1007    for (FileStatus cf : dirs) {
1008      String cfName= cf.getPath().getName();
1009      // TODO Figure out what the special dirs are
1010      if (cfName.startsWith(".") || cfName.equals(HConstants.SPLIT_LOGDIR_NAME)) continue;
1011
1012      FileStatus[] hfiles = fs.listStatus(cf.getPath());
1013      for (FileStatus hfile : hfiles) {
1014        byte[] start, end;
1015        HFile.Reader hf = null;
1016        try {
1017          hf = HFile.createReader(fs, hfile.getPath(), CacheConfig.DISABLED, true, getConf());
1018          hf.loadFileInfo();
1019          Optional<Cell> startKv = hf.getFirstKey();
1020          start = CellUtil.cloneRow(startKv.get());
1021          Optional<Cell> endKv = hf.getLastKey();
1022          end = CellUtil.cloneRow(endKv.get());
1023        } catch (IOException ioe) {
1024          LOG.warn("Problem reading orphan file " + hfile + ", skipping");
1025          continue;
1026        } catch (NullPointerException ioe) {
1027          LOG.warn("Orphan file " + hfile + " is possibly corrupted HFile, skipping");
1028          continue;
1029        } finally {
1030          if (hf != null) {
1031            hf.close();
1032          }
1033        }
1034
1035        // expand the range to include the range of all hfiles
1036        if (orphanRegionRange == null) {
1037          // first range
1038          orphanRegionRange = new Pair<>(start, end);
1039        } else {
1040          // TODO add test
1041
1042          // expand range only if the hfile is wider.
1043          if (Bytes.compareTo(orphanRegionRange.getFirst(), start) > 0) {
1044            orphanRegionRange.setFirst(start);
1045          }
1046          if (Bytes.compareTo(orphanRegionRange.getSecond(), end) < 0 ) {
1047            orphanRegionRange.setSecond(end);
1048          }
1049        }
1050      }
1051    }
1052    if (orphanRegionRange == null) {
1053      LOG.warn("No data in dir " + p + ", sidelining data");
1054      fixes++;
1055      sidelineRegionDir(fs, hi);
1056      return;
1057    }
1058    LOG.info("Min max keys are : [" + Bytes.toString(orphanRegionRange.getFirst()) + ", " +
1059        Bytes.toString(orphanRegionRange.getSecond()) + ")");
1060
1061    // create new region on hdfs. move data into place.
1062    RegionInfo regionInfo = RegionInfoBuilder.newBuilder(template.getTableName())
1063        .setStartKey(orphanRegionRange.getFirst())
1064        .setEndKey(Bytes.add(orphanRegionRange.getSecond(), new byte[1]))
1065        .build();
1066    LOG.info("Creating new region : " + regionInfo);
1067    HRegion region = HBaseFsckRepair.createHDFSRegionDir(getConf(), regionInfo, template);
1068    Path target = region.getRegionFileSystem().getRegionDir();
1069
1070    // rename all the data to new region
1071    mergeRegionDirs(target, hi);
1072    fixes++;
1073  }
1074
1075  /**
1076   * This method determines if there are table integrity errors in HDFS.  If
1077   * there are errors and the appropriate "fix" options are enabled, the method
1078   * will first correct orphan regions making them into legit regiondirs, and
1079   * then reload to merge potentially overlapping regions.
1080   *
1081   * @return number of table integrity errors found
1082   */
1083  private int restoreHdfsIntegrity() throws IOException, InterruptedException {
1084    // Determine what's on HDFS
1085    LOG.info("Loading HBase regioninfo from HDFS...");
1086    loadHdfsRegionDirs(); // populating regioninfo table.
1087
1088    int errs = errors.getErrorList().size();
1089    // First time just get suggestions.
1090    tablesInfo = loadHdfsRegionInfos(); // update tableInfos based on region info in fs.
1091    checkHdfsIntegrity(false, false);
1092
1093    if (errors.getErrorList().size() == errs) {
1094      LOG.info("No integrity errors.  We are done with this phase. Glorious.");
1095      return 0;
1096    }
1097
1098    if (shouldFixHdfsOrphans() && orphanHdfsDirs.size() > 0) {
1099      adoptHdfsOrphans(orphanHdfsDirs);
1100      // TODO optimize by incrementally adding instead of reloading.
1101    }
1102
1103    // Make sure there are no holes now.
1104    if (shouldFixHdfsHoles()) {
1105      clearState(); // this also resets # fixes.
1106      loadHdfsRegionDirs();
1107      tablesInfo = loadHdfsRegionInfos(); // update tableInfos based on region info in fs.
1108      tablesInfo = checkHdfsIntegrity(shouldFixHdfsHoles(), false);
1109    }
1110
1111    // Now we fix overlaps
1112    if (shouldFixHdfsOverlaps()) {
1113      // second pass we fix overlaps.
1114      clearState(); // this also resets # fixes.
1115      loadHdfsRegionDirs();
1116      tablesInfo = loadHdfsRegionInfos(); // update tableInfos based on region info in fs.
1117      tablesInfo = checkHdfsIntegrity(false, shouldFixHdfsOverlaps());
1118    }
1119
1120    return errors.getErrorList().size();
1121  }
1122
1123  /**
1124   * Scan all the store file names to find any lingering reference files,
1125   * which refer to some none-exiting files. If "fix" option is enabled,
1126   * any lingering reference file will be sidelined if found.
1127   * <p>
1128   * Lingering reference file prevents a region from opening. It has to
1129   * be fixed before a cluster can start properly.
1130   */
1131  private void offlineReferenceFileRepair() throws IOException, InterruptedException {
1132    clearState();
1133    Configuration conf = getConf();
1134    Path hbaseRoot = FSUtils.getRootDir(conf);
1135    FileSystem fs = hbaseRoot.getFileSystem(conf);
1136    LOG.info("Computing mapping of all store files");
1137    Map<String, Path> allFiles = FSUtils.getTableStoreFilePathMap(fs, hbaseRoot,
1138      new FSUtils.ReferenceFileFilter(fs), executor, errors);
1139    errors.print("");
1140    LOG.info("Validating mapping using HDFS state");
1141    for (Path path: allFiles.values()) {
1142      Path referredToFile = StoreFileInfo.getReferredToFile(path);
1143      if (fs.exists(referredToFile)) continue;  // good, expected
1144
1145      // Found a lingering reference file
1146      errors.reportError(ERROR_CODE.LINGERING_REFERENCE_HFILE,
1147        "Found lingering reference file " + path);
1148      if (!shouldFixReferenceFiles()) continue;
1149
1150      // Now, trying to fix it since requested
1151      boolean success = false;
1152      String pathStr = path.toString();
1153
1154      // A reference file path should be like
1155      // ${hbase.rootdir}/data/namespace/table_name/region_id/family_name/referred_file.region_name
1156      // Up 5 directories to get the root folder.
1157      // So the file will be sidelined to a similar folder structure.
1158      int index = pathStr.lastIndexOf(Path.SEPARATOR_CHAR);
1159      for (int i = 0; index > 0 && i < 5; i++) {
1160        index = pathStr.lastIndexOf(Path.SEPARATOR_CHAR, index - 1);
1161      }
1162      if (index > 0) {
1163        Path rootDir = getSidelineDir();
1164        Path dst = new Path(rootDir, pathStr.substring(index + 1));
1165        fs.mkdirs(dst.getParent());
1166        LOG.info("Trying to sideline reference file "
1167          + path + " to " + dst);
1168        setShouldRerun();
1169
1170        success = fs.rename(path, dst);
1171        debugLsr(dst);
1172
1173      }
1174      if (!success) {
1175        LOG.error("Failed to sideline reference file " + path);
1176      }
1177    }
1178  }
1179
1180  /**
1181   * Scan all the store file names to find any lingering HFileLink files,
1182   * which refer to some none-exiting files. If "fix" option is enabled,
1183   * any lingering HFileLink file will be sidelined if found.
1184   */
1185  private void offlineHLinkFileRepair() throws IOException, InterruptedException {
1186    Configuration conf = getConf();
1187    Path hbaseRoot = FSUtils.getRootDir(conf);
1188    FileSystem fs = hbaseRoot.getFileSystem(conf);
1189    LOG.info("Computing mapping of all link files");
1190    Map<String, Path> allFiles = FSUtils
1191        .getTableStoreFilePathMap(fs, hbaseRoot, new FSUtils.HFileLinkFilter(), executor, errors);
1192    errors.print("");
1193
1194    LOG.info("Validating mapping using HDFS state");
1195    for (Path path : allFiles.values()) {
1196      // building HFileLink object to gather locations
1197      HFileLink actualLink = HFileLink.buildFromHFileLinkPattern(conf, path);
1198      if (actualLink.exists(fs)) continue; // good, expected
1199
1200      // Found a lingering HFileLink
1201      errors.reportError(ERROR_CODE.LINGERING_HFILELINK, "Found lingering HFileLink " + path);
1202      if (!shouldFixHFileLinks()) continue;
1203
1204      // Now, trying to fix it since requested
1205      setShouldRerun();
1206
1207      // An HFileLink path should be like
1208      // ${hbase.rootdir}/data/namespace/table_name/region_id/family_name/linkedtable=linkedregionname-linkedhfilename
1209      // sidelineing will happen in the ${hbase.rootdir}/${sidelinedir} directory with the same folder structure.
1210      boolean success = sidelineFile(fs, hbaseRoot, path);
1211
1212      if (!success) {
1213        LOG.error("Failed to sideline HFileLink file " + path);
1214      }
1215
1216      // An HFileLink backreference path should be like
1217      // ${hbase.rootdir}/archive/data/namespace/table_name/region_id/family_name/.links-linkedhfilename
1218      // sidelineing will happen in the ${hbase.rootdir}/${sidelinedir} directory with the same folder structure.
1219      Path backRefPath = FileLink.getBackReferencesDir(HFileArchiveUtil
1220              .getStoreArchivePath(conf, HFileLink.getReferencedTableName(path.getName().toString()),
1221                  HFileLink.getReferencedRegionName(path.getName().toString()),
1222                  path.getParent().getName()),
1223          HFileLink.getReferencedHFileName(path.getName().toString()));
1224      success = sidelineFile(fs, hbaseRoot, backRefPath);
1225
1226      if (!success) {
1227        LOG.error("Failed to sideline HFileLink backreference file " + path);
1228      }
1229    }
1230  }
1231
1232  private boolean sidelineFile(FileSystem fs, Path hbaseRoot, Path path) throws IOException {
1233    URI uri = hbaseRoot.toUri().relativize(path.toUri());
1234    if (uri.isAbsolute()) return false;
1235    String relativePath = uri.getPath();
1236    Path rootDir = getSidelineDir();
1237    Path dst = new Path(rootDir, relativePath);
1238    boolean pathCreated = fs.mkdirs(dst.getParent());
1239    if (!pathCreated) {
1240      LOG.error("Failed to create path: " + dst.getParent());
1241      return false;
1242    }
1243    LOG.info("Trying to sideline file " + path + " to " + dst);
1244    return fs.rename(path, dst);
1245  }
1246
1247  /**
1248   * TODO -- need to add tests for this.
1249   */
1250  private void reportEmptyMetaCells() {
1251    errors.print("Number of empty REGIONINFO_QUALIFIER rows in hbase:meta: " +
1252      emptyRegionInfoQualifiers.size());
1253    if (details) {
1254      for (Result r: emptyRegionInfoQualifiers) {
1255        errors.print("  " + r);
1256      }
1257    }
1258  }
1259
1260  /**
1261   * TODO -- need to add tests for this.
1262   */
1263  private void reportTablesInFlux() {
1264    AtomicInteger numSkipped = new AtomicInteger(0);
1265    TableDescriptor[] allTables = getTables(numSkipped);
1266    errors.print("Number of Tables: " + allTables.length);
1267    if (details) {
1268      if (numSkipped.get() > 0) {
1269        errors.detail("Number of Tables in flux: " + numSkipped.get());
1270      }
1271      for (TableDescriptor td : allTables) {
1272        errors.detail("  Table: " + td.getTableName() + "\t" +
1273                           (td.isReadOnly() ? "ro" : "rw") + "\t" +
1274                            (td.isMetaRegion() ? "META" : "    ") + "\t" +
1275                           " families: " + td.getColumnFamilyCount());
1276      }
1277    }
1278  }
1279
1280  public ErrorReporter getErrors() {
1281    return errors;
1282  }
1283
1284  /**
1285   * Read the .regioninfo file from the file system.  If there is no
1286   * .regioninfo, add it to the orphan hdfs region list.
1287   */
1288  private void loadHdfsRegioninfo(HbckInfo hbi) throws IOException {
1289    Path regionDir = hbi.getHdfsRegionDir();
1290    if (regionDir == null) {
1291      if (hbi.getReplicaId() == RegionInfo.DEFAULT_REPLICA_ID) {
1292        // Log warning only for default/ primary replica with no region dir
1293        LOG.warn("No HDFS region dir found: " + hbi + " meta=" + hbi.metaEntry);
1294      }
1295      return;
1296    }
1297
1298    if (hbi.hdfsEntry.hri != null) {
1299      // already loaded data
1300      return;
1301    }
1302
1303    FileSystem fs = FileSystem.get(getConf());
1304    RegionInfo hri = HRegionFileSystem.loadRegionInfoFileContent(fs, regionDir);
1305    LOG.debug("RegionInfo read: " + hri.toString());
1306    hbi.hdfsEntry.hri = hri;
1307  }
1308
1309  /**
1310   * Exception thrown when a integrity repair operation fails in an
1311   * unresolvable way.
1312   */
1313  public static class RegionRepairException extends IOException {
1314    private static final long serialVersionUID = 1L;
1315    final IOException ioe;
1316    public RegionRepairException(String s, IOException ioe) {
1317      super(s);
1318      this.ioe = ioe;
1319    }
1320  }
1321
1322  /**
1323   * Populate hbi's from regionInfos loaded from file system.
1324   */
1325  private SortedMap<TableName, TableInfo> loadHdfsRegionInfos()
1326      throws IOException, InterruptedException {
1327    tablesInfo.clear(); // regenerating the data
1328    // generate region split structure
1329    Collection<HbckInfo> hbckInfos = regionInfoMap.values();
1330
1331    // Parallelized read of .regioninfo files.
1332    List<WorkItemHdfsRegionInfo> hbis = new ArrayList<>(hbckInfos.size());
1333    List<Future<Void>> hbiFutures;
1334
1335    for (HbckInfo hbi : hbckInfos) {
1336      WorkItemHdfsRegionInfo work = new WorkItemHdfsRegionInfo(hbi, this, errors);
1337      hbis.add(work);
1338    }
1339
1340    // Submit and wait for completion
1341    hbiFutures = executor.invokeAll(hbis);
1342
1343    for(int i=0; i<hbiFutures.size(); i++) {
1344      WorkItemHdfsRegionInfo work = hbis.get(i);
1345      Future<Void> f = hbiFutures.get(i);
1346      try {
1347        f.get();
1348      } catch(ExecutionException e) {
1349        LOG.warn("Failed to read .regioninfo file for region " +
1350              work.hbi.getRegionNameAsString(), e.getCause());
1351      }
1352    }
1353
1354    Path hbaseRoot = FSUtils.getRootDir(getConf());
1355    FileSystem fs = hbaseRoot.getFileSystem(getConf());
1356    // serialized table info gathering.
1357    for (HbckInfo hbi: hbckInfos) {
1358
1359      if (hbi.getHdfsHRI() == null) {
1360        // was an orphan
1361        continue;
1362      }
1363
1364
1365      // get table name from hdfs, populate various HBaseFsck tables.
1366      TableName tableName = hbi.getTableName();
1367      if (tableName == null) {
1368        // There was an entry in hbase:meta not in the HDFS?
1369        LOG.warn("tableName was null for: " + hbi);
1370        continue;
1371      }
1372
1373      TableInfo modTInfo = tablesInfo.get(tableName);
1374      if (modTInfo == null) {
1375        // only executed once per table.
1376        modTInfo = new TableInfo(tableName);
1377        tablesInfo.put(tableName, modTInfo);
1378        try {
1379          TableDescriptor htd =
1380              FSTableDescriptors.getTableDescriptorFromFs(fs, hbaseRoot, tableName);
1381          modTInfo.htds.add(htd);
1382        } catch (IOException ioe) {
1383          if (!orphanTableDirs.containsKey(tableName)) {
1384            LOG.warn("Unable to read .tableinfo from " + hbaseRoot, ioe);
1385            //should only report once for each table
1386            errors.reportError(ERROR_CODE.NO_TABLEINFO_FILE,
1387                "Unable to read .tableinfo from " + hbaseRoot + "/" + tableName);
1388            Set<String> columns = new HashSet<>();
1389            orphanTableDirs.put(tableName, getColumnFamilyList(columns, hbi));
1390          }
1391        }
1392      }
1393      if (!hbi.isSkipChecks()) {
1394        modTInfo.addRegionInfo(hbi);
1395      }
1396    }
1397
1398    loadTableInfosForTablesWithNoRegion();
1399    errors.print("");
1400
1401    return tablesInfo;
1402  }
1403
1404  /**
1405   * To get the column family list according to the column family dirs
1406   * @param columns
1407   * @param hbi
1408   * @return a set of column families
1409   * @throws IOException
1410   */
1411  private Set<String> getColumnFamilyList(Set<String> columns, HbckInfo hbi) throws IOException {
1412    Path regionDir = hbi.getHdfsRegionDir();
1413    FileSystem fs = regionDir.getFileSystem(getConf());
1414    FileStatus[] subDirs = fs.listStatus(regionDir, new FSUtils.FamilyDirFilter(fs));
1415    for (FileStatus subdir : subDirs) {
1416      String columnfamily = subdir.getPath().getName();
1417      columns.add(columnfamily);
1418    }
1419    return columns;
1420  }
1421
1422  /**
1423   * To fabricate a .tableinfo file with following contents<br>
1424   * 1. the correct tablename <br>
1425   * 2. the correct colfamily list<br>
1426   * 3. the default properties for both {@link TableDescriptor} and {@link ColumnFamilyDescriptor}<br>
1427   * @throws IOException
1428   */
1429  private boolean fabricateTableInfo(FSTableDescriptors fstd, TableName tableName,
1430      Set<String> columns) throws IOException {
1431    if (columns ==null || columns.isEmpty()) return false;
1432    TableDescriptorBuilder builder = TableDescriptorBuilder.newBuilder(tableName);
1433    for (String columnfamimly : columns) {
1434      builder.setColumnFamily(ColumnFamilyDescriptorBuilder.of(columnfamimly));
1435    }
1436    fstd.createTableDescriptor(builder.build(), true);
1437    return true;
1438  }
1439
1440  /**
1441   * To fix the empty REGIONINFO_QUALIFIER rows from hbase:meta <br>
1442   * @throws IOException
1443   */
1444  public void fixEmptyMetaCells() throws IOException {
1445    if (shouldFixEmptyMetaCells() && !emptyRegionInfoQualifiers.isEmpty()) {
1446      LOG.info("Trying to fix empty REGIONINFO_QUALIFIER hbase:meta rows.");
1447      for (Result region : emptyRegionInfoQualifiers) {
1448        deleteMetaRegion(region.getRow());
1449        errors.getErrorList().remove(ERROR_CODE.EMPTY_META_CELL);
1450      }
1451      emptyRegionInfoQualifiers.clear();
1452    }
1453  }
1454
1455  /**
1456   * To fix orphan table by creating a .tableinfo file under tableDir <br>
1457   * 1. if TableInfo is cached, to recover the .tableinfo accordingly <br>
1458   * 2. else create a default .tableinfo file with following items<br>
1459   * &nbsp;2.1 the correct tablename <br>
1460   * &nbsp;2.2 the correct colfamily list<br>
1461   * &nbsp;2.3 the default properties for both {@link TableDescriptor} and {@link ColumnFamilyDescriptor}<br>
1462   * @throws IOException
1463   */
1464  public void fixOrphanTables() throws IOException {
1465    if (shouldFixTableOrphans() && !orphanTableDirs.isEmpty()) {
1466
1467      List<TableName> tmpList = new ArrayList<>(orphanTableDirs.keySet().size());
1468      tmpList.addAll(orphanTableDirs.keySet());
1469      TableDescriptor[] htds = getTableDescriptors(tmpList);
1470      Iterator<Entry<TableName, Set<String>>> iter =
1471          orphanTableDirs.entrySet().iterator();
1472      int j = 0;
1473      int numFailedCase = 0;
1474      FSTableDescriptors fstd = new FSTableDescriptors(getConf());
1475      while (iter.hasNext()) {
1476        Entry<TableName, Set<String>> entry =
1477            iter.next();
1478        TableName tableName = entry.getKey();
1479        LOG.info("Trying to fix orphan table error: " + tableName);
1480        if (j < htds.length) {
1481          if (tableName.equals(htds[j].getTableName())) {
1482            TableDescriptor htd = htds[j];
1483            LOG.info("fixing orphan table: " + tableName + " from cache");
1484            fstd.createTableDescriptor(htd, true);
1485            j++;
1486            iter.remove();
1487          }
1488        } else {
1489          if (fabricateTableInfo(fstd, tableName, entry.getValue())) {
1490            LOG.warn("fixing orphan table: " + tableName + " with a default .tableinfo file");
1491            LOG.warn("Strongly recommend to modify the TableDescriptor if necessary for: " + tableName);
1492            iter.remove();
1493          } else {
1494            LOG.error("Unable to create default .tableinfo for " + tableName + " while missing column family information");
1495            numFailedCase++;
1496          }
1497        }
1498        fixes++;
1499      }
1500
1501      if (orphanTableDirs.isEmpty()) {
1502        // all orphanTableDirs are luckily recovered
1503        // re-run doFsck after recovering the .tableinfo file
1504        setShouldRerun();
1505        LOG.warn("Strongly recommend to re-run manually hfsck after all orphanTableDirs being fixed");
1506      } else if (numFailedCase > 0) {
1507        LOG.error("Failed to fix " + numFailedCase
1508            + " OrphanTables with default .tableinfo files");
1509      }
1510
1511    }
1512    //cleanup the list
1513    orphanTableDirs.clear();
1514
1515  }
1516
1517  /**
1518   * This borrows code from MasterFileSystem.bootstrap(). Explicitly creates it's own WAL, so be
1519   * sure to close it as well as the region when you're finished.
1520   * @param walFactoryID A unique identifier for WAL factory. Filesystem implementations will use
1521   *          this ID to make a directory inside WAL directory path.
1522   * @return an open hbase:meta HRegion
1523   */
1524  private HRegion createNewMeta(String walFactoryID) throws IOException {
1525    Path rootdir = FSUtils.getRootDir(getConf());
1526    Configuration c = getConf();
1527    RegionInfo metaHRI = RegionInfoBuilder.FIRST_META_REGIONINFO;
1528    TableDescriptor metaDescriptor = new FSTableDescriptors(c).get(TableName.META_TABLE_NAME);
1529    MasterFileSystem.setInfoFamilyCachingForMeta(metaDescriptor, false);
1530    // The WAL subsystem will use the default rootDir rather than the passed in rootDir
1531    // unless I pass along via the conf.
1532    Configuration confForWAL = new Configuration(c);
1533    confForWAL.set(HConstants.HBASE_DIR, rootdir.toString());
1534    WAL wal = new WALFactory(confForWAL, walFactoryID).getWAL(metaHRI);
1535    HRegion meta = HRegion.createHRegion(metaHRI, rootdir, c, metaDescriptor, wal);
1536    MasterFileSystem.setInfoFamilyCachingForMeta(metaDescriptor, true);
1537    return meta;
1538  }
1539
1540  /**
1541   * Generate set of puts to add to new meta.  This expects the tables to be
1542   * clean with no overlaps or holes.  If there are any problems it returns null.
1543   *
1544   * @return An array list of puts to do in bulk, null if tables have problems
1545   */
1546  private ArrayList<Put> generatePuts(SortedMap<TableName, TableInfo> tablesInfo)
1547      throws IOException {
1548    ArrayList<Put> puts = new ArrayList<>();
1549    boolean hasProblems = false;
1550    for (Entry<TableName, TableInfo> e : tablesInfo.entrySet()) {
1551      TableName name = e.getKey();
1552
1553      // skip "hbase:meta"
1554      if (name.compareTo(TableName.META_TABLE_NAME) == 0) {
1555        continue;
1556      }
1557
1558      TableInfo ti = e.getValue();
1559      puts.add(MetaTableAccessor.makePutFromTableState(
1560        new TableState(ti.tableName, TableState.State.ENABLED),
1561        EnvironmentEdgeManager.currentTime()));
1562      for (Entry<byte[], Collection<HbckInfo>> spl : ti.sc.getStarts().asMap()
1563          .entrySet()) {
1564        Collection<HbckInfo> his = spl.getValue();
1565        int sz = his.size();
1566        if (sz != 1) {
1567          // problem
1568          LOG.error("Split starting at " + Bytes.toStringBinary(spl.getKey())
1569              + " had " +  sz + " regions instead of exactly 1." );
1570          hasProblems = true;
1571          continue;
1572        }
1573
1574        // add the row directly to meta.
1575        HbckInfo hi = his.iterator().next();
1576        RegionInfo hri = hi.getHdfsHRI(); // hi.metaEntry;
1577        Put p = MetaTableAccessor.makePutFromRegionInfo(hri, EnvironmentEdgeManager.currentTime());
1578        puts.add(p);
1579      }
1580    }
1581    return hasProblems ? null : puts;
1582  }
1583
1584  /**
1585   * Suggest fixes for each table
1586   */
1587  private void suggestFixes(
1588      SortedMap<TableName, TableInfo> tablesInfo) throws IOException {
1589    logParallelMerge();
1590    for (TableInfo tInfo : tablesInfo.values()) {
1591      TableIntegrityErrorHandler handler = tInfo.new IntegrityFixSuggester(tInfo, errors);
1592      tInfo.checkRegionChain(handler);
1593    }
1594  }
1595
1596  /**
1597   * Rebuilds meta from information in hdfs/fs.  Depends on configuration settings passed into
1598   * hbck constructor to point to a particular fs/dir. Assumes HBase is OFFLINE.
1599   *
1600   * @param fix flag that determines if method should attempt to fix holes
1601   * @return true if successful, false if attempt failed.
1602   */
1603  public boolean rebuildMeta(boolean fix) throws IOException,
1604      InterruptedException {
1605
1606    // TODO check to make sure hbase is offline. (or at least the table
1607    // currently being worked on is off line)
1608
1609    // Determine what's on HDFS
1610    LOG.info("Loading HBase regioninfo from HDFS...");
1611    loadHdfsRegionDirs(); // populating regioninfo table.
1612
1613    int errs = errors.getErrorList().size();
1614    tablesInfo = loadHdfsRegionInfos(); // update tableInfos based on region info in fs.
1615    checkHdfsIntegrity(false, false);
1616
1617    // make sure ok.
1618    if (errors.getErrorList().size() != errs) {
1619      // While in error state, iterate until no more fixes possible
1620      while(true) {
1621        fixes = 0;
1622        suggestFixes(tablesInfo);
1623        errors.clear();
1624        loadHdfsRegionInfos(); // update tableInfos based on region info in fs.
1625        checkHdfsIntegrity(shouldFixHdfsHoles(), shouldFixHdfsOverlaps());
1626
1627        int errCount = errors.getErrorList().size();
1628
1629        if (fixes == 0) {
1630          if (errCount > 0) {
1631            return false; // failed to fix problems.
1632          } else {
1633            break; // no fixes and no problems? drop out and fix stuff!
1634          }
1635        }
1636      }
1637    }
1638
1639    // we can rebuild, move old meta out of the way and start
1640    LOG.info("HDFS regioninfo's seems good.  Sidelining old hbase:meta");
1641    Path backupDir = sidelineOldMeta();
1642
1643    LOG.info("Creating new hbase:meta");
1644    String walFactoryId = "hbck-meta-recovery-" + RandomStringUtils.randomNumeric(8);
1645    HRegion meta = createNewMeta(walFactoryId);
1646
1647    // populate meta
1648    List<Put> puts = generatePuts(tablesInfo);
1649    if (puts == null) {
1650      LOG.error(HBaseMarkers.FATAL, "Problem encountered when creating new hbase:meta "
1651          + "entries. You may need to restore the previously sidelined hbase:meta");
1652      return false;
1653    }
1654    meta.batchMutate(puts.toArray(new Put[puts.size()]), HConstants.NO_NONCE, HConstants.NO_NONCE);
1655    meta.close();
1656    if (meta.getWAL() != null) {
1657      meta.getWAL().close();
1658    }
1659    // clean up the temporary hbck meta recovery WAL directory
1660    removeHBCKMetaRecoveryWALDir(walFactoryId);
1661    LOG.info("Success! hbase:meta table rebuilt.");
1662    LOG.info("Old hbase:meta is moved into " + backupDir);
1663    return true;
1664  }
1665
1666  /**
1667   * Removes the empty Meta recovery WAL directory.
1668   * @param walFactoryId A unique identifier for WAL factory which was used by Filesystem to make a
1669   *          Meta recovery WAL directory inside WAL directory path.
1670   */
1671  private void removeHBCKMetaRecoveryWALDir(String walFactoryId) throws IOException {
1672    Path rootdir = FSUtils.getRootDir(getConf());
1673    Path walLogDir = new Path(new Path(rootdir, HConstants.HREGION_LOGDIR_NAME), walFactoryId);
1674    FileSystem fs = FSUtils.getCurrentFileSystem(getConf());
1675    FileStatus[] walFiles = FSUtils.listStatus(fs, walLogDir, null);
1676    if (walFiles == null || walFiles.length == 0) {
1677      LOG.info("HBCK meta recovery WAL directory is empty, removing it now.");
1678      if (!FSUtils.deleteDirectory(fs, walLogDir)) {
1679        LOG.warn("Couldn't clear the HBCK Meta recovery WAL directory " + walLogDir);
1680      }
1681    }
1682  }
1683
1684  /**
1685   * Log an appropriate message about whether or not overlapping merges are computed in parallel.
1686   */
1687  private void logParallelMerge() {
1688    if (getConf().getBoolean("hbasefsck.overlap.merge.parallel", true)) {
1689      LOG.info("Handling overlap merges in parallel. set hbasefsck.overlap.merge.parallel to" +
1690          " false to run serially.");
1691    } else {
1692      LOG.info("Handling overlap merges serially.  set hbasefsck.overlap.merge.parallel to" +
1693          " true to run in parallel.");
1694    }
1695  }
1696
1697  private SortedMap<TableName, TableInfo> checkHdfsIntegrity(boolean fixHoles,
1698      boolean fixOverlaps) throws IOException {
1699    LOG.info("Checking HBase region split map from HDFS data...");
1700    logParallelMerge();
1701    for (TableInfo tInfo : tablesInfo.values()) {
1702      TableIntegrityErrorHandler handler;
1703      if (fixHoles || fixOverlaps) {
1704        handler = tInfo.new HDFSIntegrityFixer(tInfo, errors, getConf(),
1705          fixHoles, fixOverlaps);
1706      } else {
1707        handler = tInfo.new IntegrityFixSuggester(tInfo, errors);
1708      }
1709      if (!tInfo.checkRegionChain(handler)) {
1710        // should dump info as well.
1711        errors.report("Found inconsistency in table " + tInfo.getName());
1712      }
1713    }
1714    return tablesInfo;
1715  }
1716
1717  private Path getSidelineDir() throws IOException {
1718    if (sidelineDir == null) {
1719      Path hbaseDir = FSUtils.getRootDir(getConf());
1720      Path hbckDir = new Path(hbaseDir, HConstants.HBCK_SIDELINEDIR_NAME);
1721      sidelineDir = new Path(hbckDir, hbaseDir.getName() + "-"
1722          + startMillis);
1723    }
1724    return sidelineDir;
1725  }
1726
1727  /**
1728   * Sideline a region dir (instead of deleting it)
1729   */
1730  Path sidelineRegionDir(FileSystem fs, HbckInfo hi) throws IOException {
1731    return sidelineRegionDir(fs, null, hi);
1732  }
1733
1734  /**
1735   * Sideline a region dir (instead of deleting it)
1736   *
1737   * @param parentDir if specified, the region will be sidelined to folder like
1738   *     {@literal .../parentDir/<table name>/<region name>}. The purpose is to group together
1739   *     similar regions sidelined, for example, those regions should be bulk loaded back later
1740   *     on. If NULL, it is ignored.
1741   */
1742  Path sidelineRegionDir(FileSystem fs,
1743      String parentDir, HbckInfo hi) throws IOException {
1744    TableName tableName = hi.getTableName();
1745    Path regionDir = hi.getHdfsRegionDir();
1746
1747    if (!fs.exists(regionDir)) {
1748      LOG.warn("No previous " + regionDir + " exists.  Continuing.");
1749      return null;
1750    }
1751
1752    Path rootDir = getSidelineDir();
1753    if (parentDir != null) {
1754      rootDir = new Path(rootDir, parentDir);
1755    }
1756    Path sidelineTableDir= FSUtils.getTableDir(rootDir, tableName);
1757    Path sidelineRegionDir = new Path(sidelineTableDir, regionDir.getName());
1758    fs.mkdirs(sidelineRegionDir);
1759    boolean success = false;
1760    FileStatus[] cfs =  fs.listStatus(regionDir);
1761    if (cfs == null) {
1762      LOG.info("Region dir is empty: " + regionDir);
1763    } else {
1764      for (FileStatus cf : cfs) {
1765        Path src = cf.getPath();
1766        Path dst =  new Path(sidelineRegionDir, src.getName());
1767        if (fs.isFile(src)) {
1768          // simple file
1769          success = fs.rename(src, dst);
1770          if (!success) {
1771            String msg = "Unable to rename file " + src +  " to " + dst;
1772            LOG.error(msg);
1773            throw new IOException(msg);
1774          }
1775          continue;
1776        }
1777
1778        // is a directory.
1779        fs.mkdirs(dst);
1780
1781        LOG.info("Sidelining files from " + src + " into containing region " + dst);
1782        // FileSystem.rename is inconsistent with directories -- if the
1783        // dst (foo/a) exists and is a dir, and the src (foo/b) is a dir,
1784        // it moves the src into the dst dir resulting in (foo/a/b).  If
1785        // the dst does not exist, and the src a dir, src becomes dst. (foo/b)
1786        FileStatus[] hfiles = fs.listStatus(src);
1787        if (hfiles != null && hfiles.length > 0) {
1788          for (FileStatus hfile : hfiles) {
1789            success = fs.rename(hfile.getPath(), dst);
1790            if (!success) {
1791              String msg = "Unable to rename file " + src +  " to " + dst;
1792              LOG.error(msg);
1793              throw new IOException(msg);
1794            }
1795          }
1796        }
1797        LOG.debug("Sideline directory contents:");
1798        debugLsr(sidelineRegionDir);
1799      }
1800    }
1801
1802    LOG.info("Removing old region dir: " + regionDir);
1803    success = fs.delete(regionDir, true);
1804    if (!success) {
1805      String msg = "Unable to delete dir " + regionDir;
1806      LOG.error(msg);
1807      throw new IOException(msg);
1808    }
1809    return sidelineRegionDir;
1810  }
1811
1812  /**
1813   * Side line an entire table.
1814   */
1815  void sidelineTable(FileSystem fs, TableName tableName, Path hbaseDir,
1816      Path backupHbaseDir) throws IOException {
1817    Path tableDir = FSUtils.getTableDir(hbaseDir, tableName);
1818    if (fs.exists(tableDir)) {
1819      Path backupTableDir= FSUtils.getTableDir(backupHbaseDir, tableName);
1820      fs.mkdirs(backupTableDir.getParent());
1821      boolean success = fs.rename(tableDir, backupTableDir);
1822      if (!success) {
1823        throw new IOException("Failed to move  " + tableName + " from "
1824            +  tableDir + " to " + backupTableDir);
1825      }
1826    } else {
1827      LOG.info("No previous " + tableName +  " exists.  Continuing.");
1828    }
1829  }
1830
1831  /**
1832   * @return Path to backup of original directory
1833   */
1834  Path sidelineOldMeta() throws IOException {
1835    // put current hbase:meta aside.
1836    Path hbaseDir = FSUtils.getRootDir(getConf());
1837    FileSystem fs = hbaseDir.getFileSystem(getConf());
1838    Path backupDir = getSidelineDir();
1839    fs.mkdirs(backupDir);
1840
1841    try {
1842      sidelineTable(fs, TableName.META_TABLE_NAME, hbaseDir, backupDir);
1843    } catch (IOException e) {
1844        LOG.error(HBaseMarkers.FATAL, "... failed to sideline meta. Currently in "
1845            + "inconsistent state.  To restore try to rename hbase:meta in " +
1846            backupDir.getName() + " to " + hbaseDir.getName() + ".", e);
1847      throw e; // throw original exception
1848    }
1849    return backupDir;
1850  }
1851
1852  /**
1853   * Load the list of disabled tables in ZK into local set.
1854   * @throws ZooKeeperConnectionException
1855   * @throws IOException
1856   */
1857  private void loadTableStates()
1858  throws IOException {
1859    tableStates = MetaTableAccessor.getTableStates(connection);
1860    // Add hbase:meta so this tool keeps working. In hbase2, meta is always enabled though it
1861    // has no entry in the table states. HBCK doesn't work right w/ hbase2 but just do this in
1862    // meantime.
1863    this.tableStates.put(TableName.META_TABLE_NAME,
1864        new TableState(TableName.META_TABLE_NAME, TableState.State.ENABLED));
1865  }
1866
1867  /**
1868   * Check if the specified region's table is disabled.
1869   * @param tableName table to check status of
1870   */
1871  private boolean isTableDisabled(TableName tableName) {
1872    return tableStates.containsKey(tableName)
1873        && tableStates.get(tableName)
1874        .inStates(TableState.State.DISABLED, TableState.State.DISABLING);
1875  }
1876
1877  /**
1878   * Scan HDFS for all regions, recording their information into
1879   * regionInfoMap
1880   */
1881  public void loadHdfsRegionDirs() throws IOException, InterruptedException {
1882    Path rootDir = FSUtils.getRootDir(getConf());
1883    FileSystem fs = rootDir.getFileSystem(getConf());
1884
1885    // list all tables from HDFS
1886    List<FileStatus> tableDirs = Lists.newArrayList();
1887
1888    boolean foundVersionFile = fs.exists(new Path(rootDir, HConstants.VERSION_FILE_NAME));
1889
1890    List<Path> paths = FSUtils.getTableDirs(fs, rootDir);
1891    for (Path path : paths) {
1892      TableName tableName = FSUtils.getTableName(path);
1893       if ((!checkMetaOnly &&
1894           isTableIncluded(tableName)) ||
1895           tableName.equals(TableName.META_TABLE_NAME)) {
1896         tableDirs.add(fs.getFileStatus(path));
1897       }
1898    }
1899
1900    // verify that version file exists
1901    if (!foundVersionFile) {
1902      errors.reportError(ERROR_CODE.NO_VERSION_FILE,
1903          "Version file does not exist in root dir " + rootDir);
1904      if (shouldFixVersionFile()) {
1905        LOG.info("Trying to create a new " + HConstants.VERSION_FILE_NAME
1906            + " file.");
1907        setShouldRerun();
1908        FSUtils.setVersion(fs, rootDir, getConf().getInt(
1909            HConstants.THREAD_WAKE_FREQUENCY, 10 * 1000), getConf().getInt(
1910            HConstants.VERSION_FILE_WRITE_ATTEMPTS,
1911            HConstants.DEFAULT_VERSION_FILE_WRITE_ATTEMPTS));
1912      }
1913    }
1914
1915    // Avoid multithreading at table-level because already multithreaded internally at
1916    // region-level.  Additionally multithreading at table-level can lead to deadlock
1917    // if there are many tables in the cluster.  Since there are a limited # of threads
1918    // in the executor's thread pool and if we multithread at the table-level by putting
1919    // WorkItemHdfsDir callables into the executor, then we will have some threads in the
1920    // executor tied up solely in waiting for the tables' region-level calls to complete.
1921    // If there are enough tables then there will be no actual threads in the pool left
1922    // for the region-level callables to be serviced.
1923    for (FileStatus tableDir : tableDirs) {
1924      LOG.debug("Loading region dirs from " +tableDir.getPath());
1925      WorkItemHdfsDir item = new WorkItemHdfsDir(fs, errors, tableDir);
1926      try {
1927        item.call();
1928      } catch (ExecutionException e) {
1929        LOG.warn("Could not completely load table dir " +
1930            tableDir.getPath(), e.getCause());
1931      }
1932    }
1933    errors.print("");
1934  }
1935
1936  /**
1937   * Record the location of the hbase:meta region as found in ZooKeeper.
1938   */
1939  private boolean recordMetaRegion() throws IOException {
1940    RegionLocations rl = connection.locateRegion(TableName.META_TABLE_NAME,
1941        HConstants.EMPTY_START_ROW, false, false);
1942    if (rl == null) {
1943      errors.reportError(ERROR_CODE.NULL_META_REGION,
1944          "META region was not found in ZooKeeper");
1945      return false;
1946    }
1947    for (HRegionLocation metaLocation : rl.getRegionLocations()) {
1948      // Check if Meta region is valid and existing
1949      if (metaLocation == null ) {
1950        errors.reportError(ERROR_CODE.NULL_META_REGION,
1951            "META region location is null");
1952        return false;
1953      }
1954      if (metaLocation.getRegionInfo() == null) {
1955        errors.reportError(ERROR_CODE.NULL_META_REGION,
1956            "META location regionInfo is null");
1957        return false;
1958      }
1959      if (metaLocation.getHostname() == null) {
1960        errors.reportError(ERROR_CODE.NULL_META_REGION,
1961            "META location hostName is null");
1962        return false;
1963      }
1964      ServerName sn = metaLocation.getServerName();
1965      MetaEntry m = new MetaEntry(metaLocation.getRegionInfo(), sn, EnvironmentEdgeManager.currentTime());
1966      HbckInfo hbckInfo = regionInfoMap.get(metaLocation.getRegionInfo().getEncodedName());
1967      if (hbckInfo == null) {
1968        regionInfoMap.put(metaLocation.getRegionInfo().getEncodedName(), new HbckInfo(m));
1969      } else {
1970        hbckInfo.metaEntry = m;
1971      }
1972    }
1973    return true;
1974  }
1975
1976  private ZKWatcher createZooKeeperWatcher() throws IOException {
1977    return new ZKWatcher(getConf(), "hbase Fsck", new Abortable() {
1978      @Override
1979      public void abort(String why, Throwable e) {
1980        LOG.error(why, e);
1981        System.exit(1);
1982      }
1983
1984      @Override
1985      public boolean isAborted() {
1986        return false;
1987      }
1988
1989    });
1990  }
1991
1992  private ServerName getMetaRegionServerName(int replicaId)
1993  throws IOException, KeeperException {
1994    return new MetaTableLocator().getMetaRegionLocation(zkw, replicaId);
1995  }
1996
1997  /**
1998   * Contacts each regionserver and fetches metadata about regions.
1999   * @param regionServerList - the list of region servers to connect to
2000   * @throws IOException if a remote or network exception occurs
2001   */
2002  void processRegionServers(Collection<ServerName> regionServerList)
2003    throws IOException, InterruptedException {
2004
2005    List<WorkItemRegion> workItems = new ArrayList<>(regionServerList.size());
2006    List<Future<Void>> workFutures;
2007
2008    // loop to contact each region server in parallel
2009    for (ServerName rsinfo: regionServerList) {
2010      workItems.add(new WorkItemRegion(this, rsinfo, errors, connection));
2011    }
2012
2013    workFutures = executor.invokeAll(workItems);
2014
2015    for(int i=0; i<workFutures.size(); i++) {
2016      WorkItemRegion item = workItems.get(i);
2017      Future<Void> f = workFutures.get(i);
2018      try {
2019        f.get();
2020      } catch(ExecutionException e) {
2021        LOG.warn("Could not process regionserver " + item.rsinfo.getHostAndPort(),
2022            e.getCause());
2023      }
2024    }
2025  }
2026
2027  /**
2028   * Check consistency of all regions that have been found in previous phases.
2029   */
2030  private void checkAndFixConsistency()
2031  throws IOException, KeeperException, InterruptedException {
2032    // Divide the checks in two phases. One for default/primary replicas and another
2033    // for the non-primary ones. Keeps code cleaner this way.
2034
2035    List<CheckRegionConsistencyWorkItem> workItems = new ArrayList<>(regionInfoMap.size());
2036    for (java.util.Map.Entry<String, HbckInfo> e: regionInfoMap.entrySet()) {
2037      if (e.getValue().getReplicaId() == RegionInfo.DEFAULT_REPLICA_ID) {
2038        workItems.add(new CheckRegionConsistencyWorkItem(e.getKey(), e.getValue()));
2039      }
2040    }
2041    checkRegionConsistencyConcurrently(workItems);
2042
2043    boolean prevHdfsCheck = shouldCheckHdfs();
2044    setCheckHdfs(false); //replicas don't have any hdfs data
2045    // Run a pass over the replicas and fix any assignment issues that exist on the currently
2046    // deployed/undeployed replicas.
2047    List<CheckRegionConsistencyWorkItem> replicaWorkItems = new ArrayList<>(regionInfoMap.size());
2048    for (java.util.Map.Entry<String, HbckInfo> e: regionInfoMap.entrySet()) {
2049      if (e.getValue().getReplicaId() != RegionInfo.DEFAULT_REPLICA_ID) {
2050        replicaWorkItems.add(new CheckRegionConsistencyWorkItem(e.getKey(), e.getValue()));
2051      }
2052    }
2053    checkRegionConsistencyConcurrently(replicaWorkItems);
2054    setCheckHdfs(prevHdfsCheck);
2055
2056    // If some regions is skipped during checkRegionConsistencyConcurrently() phase, we might
2057    // not get accurate state of the hbase if continuing. The config here allows users to tune
2058    // the tolerance of number of skipped region.
2059    // TODO: evaluate the consequence to continue the hbck operation without config.
2060    int terminateThreshold =  getConf().getInt("hbase.hbck.skipped.regions.limit", 0);
2061    int numOfSkippedRegions = skippedRegions.size();
2062    if (numOfSkippedRegions > 0 && numOfSkippedRegions > terminateThreshold) {
2063      throw new IOException(numOfSkippedRegions
2064        + " region(s) could not be checked or repaired.  See logs for detail.");
2065    }
2066
2067    if (shouldCheckHdfs()) {
2068      checkAndFixTableStates();
2069    }
2070  }
2071
2072  /**
2073   * Check consistency of all regions using mulitple threads concurrently.
2074   */
2075  private void checkRegionConsistencyConcurrently(
2076    final List<CheckRegionConsistencyWorkItem> workItems)
2077    throws IOException, KeeperException, InterruptedException {
2078    if (workItems.isEmpty()) {
2079      return;  // nothing to check
2080    }
2081
2082    List<Future<Void>> workFutures = executor.invokeAll(workItems);
2083    for(Future<Void> f: workFutures) {
2084      try {
2085        f.get();
2086      } catch(ExecutionException e1) {
2087        LOG.warn("Could not check region consistency " , e1.getCause());
2088        if (e1.getCause() instanceof IOException) {
2089          throw (IOException)e1.getCause();
2090        } else if (e1.getCause() instanceof KeeperException) {
2091          throw (KeeperException)e1.getCause();
2092        } else if (e1.getCause() instanceof InterruptedException) {
2093          throw (InterruptedException)e1.getCause();
2094        } else {
2095          throw new IOException(e1.getCause());
2096        }
2097      }
2098    }
2099  }
2100
2101  class CheckRegionConsistencyWorkItem implements Callable<Void> {
2102    private final String key;
2103    private final HbckInfo hbi;
2104
2105    CheckRegionConsistencyWorkItem(String key, HbckInfo hbi) {
2106      this.key = key;
2107      this.hbi = hbi;
2108    }
2109
2110    @Override
2111    public synchronized Void call() throws Exception {
2112      try {
2113        checkRegionConsistency(key, hbi);
2114      } catch (Exception e) {
2115        // If the region is non-META region, skip this region and send warning/error message; if
2116        // the region is META region, we should not continue.
2117        LOG.warn("Unable to complete check or repair the region '" + hbi.getRegionNameAsString()
2118          + "'.", e);
2119        if (hbi.getHdfsHRI().isMetaRegion()) {
2120          throw e;
2121        }
2122        LOG.warn("Skip region '" + hbi.getRegionNameAsString() + "'");
2123        addSkippedRegion(hbi);
2124      }
2125      return null;
2126    }
2127  }
2128
2129  private void addSkippedRegion(final HbckInfo hbi) {
2130    Set<String> skippedRegionNames = skippedRegions.get(hbi.getTableName());
2131    if (skippedRegionNames == null) {
2132      skippedRegionNames = new HashSet<>();
2133    }
2134    skippedRegionNames.add(hbi.getRegionNameAsString());
2135    skippedRegions.put(hbi.getTableName(), skippedRegionNames);
2136  }
2137
2138  /**
2139   * Check and fix table states, assumes full info available:
2140   * - tableInfos
2141   * - empty tables loaded
2142   */
2143  private void checkAndFixTableStates() throws IOException {
2144    // first check dangling states
2145    for (Entry<TableName, TableState> entry : tableStates.entrySet()) {
2146      TableName tableName = entry.getKey();
2147      TableState tableState = entry.getValue();
2148      TableInfo tableInfo = tablesInfo.get(tableName);
2149      if (isTableIncluded(tableName)
2150          && !tableName.isSystemTable()
2151          && tableInfo == null) {
2152        if (fixMeta) {
2153          MetaTableAccessor.deleteTableState(connection, tableName);
2154          TableState state = MetaTableAccessor.getTableState(connection, tableName);
2155          if (state != null) {
2156            errors.reportError(ERROR_CODE.ORPHAN_TABLE_STATE,
2157                tableName + " unable to delete dangling table state " + tableState);
2158          }
2159        } else if (!checkMetaOnly) {
2160          // dangling table state in meta if checkMetaOnly is false. If checkMetaOnly is
2161          // true, tableInfo will be null as tablesInfo are not polulated for all tables from hdfs
2162          errors.reportError(ERROR_CODE.ORPHAN_TABLE_STATE,
2163              tableName + " has dangling table state " + tableState);
2164        }
2165      }
2166    }
2167    // check that all tables have states
2168    for (TableName tableName : tablesInfo.keySet()) {
2169      if (isTableIncluded(tableName) && !tableStates.containsKey(tableName)) {
2170        if (fixMeta) {
2171          MetaTableAccessor.updateTableState(connection, tableName, TableState.State.ENABLED);
2172          TableState newState = MetaTableAccessor.getTableState(connection, tableName);
2173          if (newState == null) {
2174            errors.reportError(ERROR_CODE.NO_TABLE_STATE,
2175                "Unable to change state for table " + tableName + " in meta ");
2176          }
2177        } else {
2178          errors.reportError(ERROR_CODE.NO_TABLE_STATE,
2179              tableName + " has no state in meta ");
2180        }
2181      }
2182    }
2183  }
2184
2185  private void preCheckPermission() throws IOException, AccessDeniedException {
2186    if (shouldIgnorePreCheckPermission()) {
2187      return;
2188    }
2189
2190    Path hbaseDir = FSUtils.getRootDir(getConf());
2191    FileSystem fs = hbaseDir.getFileSystem(getConf());
2192    UserProvider userProvider = UserProvider.instantiate(getConf());
2193    UserGroupInformation ugi = userProvider.getCurrent().getUGI();
2194    FileStatus[] files = fs.listStatus(hbaseDir);
2195    for (FileStatus file : files) {
2196      try {
2197        FSUtils.checkAccess(ugi, file, FsAction.WRITE);
2198      } catch (AccessDeniedException ace) {
2199        LOG.warn("Got AccessDeniedException when preCheckPermission ", ace);
2200        errors.reportError(ERROR_CODE.WRONG_USAGE, "Current user " + ugi.getUserName()
2201          + " does not have write perms to " + file.getPath()
2202          + ". Please rerun hbck as hdfs user " + file.getOwner());
2203        throw ace;
2204      }
2205    }
2206  }
2207
2208  /**
2209   * Deletes region from meta table
2210   */
2211  private void deleteMetaRegion(HbckInfo hi) throws IOException {
2212    deleteMetaRegion(hi.metaEntry.getRegionName());
2213  }
2214
2215  /**
2216   * Deletes region from meta table
2217   */
2218  private void deleteMetaRegion(byte[] metaKey) throws IOException {
2219    Delete d = new Delete(metaKey);
2220    meta.delete(d);
2221    LOG.info("Deleted " + Bytes.toString(metaKey) + " from META" );
2222  }
2223
2224  /**
2225   * Reset the split parent region info in meta table
2226   */
2227  private void resetSplitParent(HbckInfo hi) throws IOException {
2228    RowMutations mutations = new RowMutations(hi.metaEntry.getRegionName());
2229    Delete d = new Delete(hi.metaEntry.getRegionName());
2230    d.addColumn(HConstants.CATALOG_FAMILY, HConstants.SPLITA_QUALIFIER);
2231    d.addColumn(HConstants.CATALOG_FAMILY, HConstants.SPLITB_QUALIFIER);
2232    mutations.add(d);
2233
2234    RegionInfo hri = RegionInfoBuilder.newBuilder(hi.metaEntry)
2235        .setOffline(false)
2236        .setSplit(false)
2237        .build();
2238    Put p = MetaTableAccessor.makePutFromRegionInfo(hri, EnvironmentEdgeManager.currentTime());
2239    mutations.add(p);
2240
2241    meta.mutateRow(mutations);
2242    LOG.info("Reset split parent " + hi.metaEntry.getRegionNameAsString() + " in META" );
2243  }
2244
2245  /**
2246   * This backwards-compatibility wrapper for permanently offlining a region
2247   * that should not be alive.  If the region server does not support the
2248   * "offline" method, it will use the closest unassign method instead.  This
2249   * will basically work until one attempts to disable or delete the affected
2250   * table.  The problem has to do with in-memory only master state, so
2251   * restarting the HMaster or failing over to another should fix this.
2252   */
2253  private void offline(byte[] regionName) throws IOException {
2254    String regionString = Bytes.toStringBinary(regionName);
2255    if (!rsSupportsOffline) {
2256      LOG.warn("Using unassign region " + regionString
2257          + " instead of using offline method, you should"
2258          + " restart HMaster after these repairs");
2259      admin.unassign(regionName, true);
2260      return;
2261    }
2262
2263    // first time we assume the rs's supports #offline.
2264    try {
2265      LOG.info("Offlining region " + regionString);
2266      admin.offline(regionName);
2267    } catch (IOException ioe) {
2268      String notFoundMsg = "java.lang.NoSuchMethodException: " +
2269        "org.apache.hadoop.hbase.master.HMaster.offline([B)";
2270      if (ioe.getMessage().contains(notFoundMsg)) {
2271        LOG.warn("Using unassign region " + regionString
2272            + " instead of using offline method, you should"
2273            + " restart HMaster after these repairs");
2274        rsSupportsOffline = false; // in the future just use unassign
2275        admin.unassign(regionName, true);
2276        return;
2277      }
2278      throw ioe;
2279    }
2280  }
2281
2282  private void undeployRegions(HbckInfo hi) throws IOException, InterruptedException {
2283    undeployRegionsForHbi(hi);
2284    // undeploy replicas of the region (but only if the method is invoked for the primary)
2285    if (hi.getReplicaId() != RegionInfo.DEFAULT_REPLICA_ID) {
2286      return;
2287    }
2288    int numReplicas = admin.getTableDescriptor(hi.getTableName()).getRegionReplication();
2289    for (int i = 1; i < numReplicas; i++) {
2290      if (hi.getPrimaryHRIForDeployedReplica() == null) continue;
2291      RegionInfo hri = RegionReplicaUtil.getRegionInfoForReplica(
2292          hi.getPrimaryHRIForDeployedReplica(), i);
2293      HbckInfo h = regionInfoMap.get(hri.getEncodedName());
2294      if (h != null) {
2295        undeployRegionsForHbi(h);
2296        //set skip checks; we undeployed it, and we don't want to evaluate this anymore
2297        //in consistency checks
2298        h.setSkipChecks(true);
2299      }
2300    }
2301  }
2302
2303  private void undeployRegionsForHbi(HbckInfo hi) throws IOException, InterruptedException {
2304    for (OnlineEntry rse : hi.deployedEntries) {
2305      LOG.debug("Undeploy region "  + rse.hri + " from " + rse.hsa);
2306      try {
2307        HBaseFsckRepair.closeRegionSilentlyAndWait(connection, rse.hsa, rse.hri);
2308        offline(rse.hri.getRegionName());
2309      } catch (IOException ioe) {
2310        LOG.warn("Got exception when attempting to offline region "
2311            + Bytes.toString(rse.hri.getRegionName()), ioe);
2312      }
2313    }
2314  }
2315
2316  /**
2317   * Attempts to undeploy a region from a region server based in information in
2318   * META.  Any operations that modify the file system should make sure that
2319   * its corresponding region is not deployed to prevent data races.
2320   *
2321   * A separate call is required to update the master in-memory region state
2322   * kept in the AssignementManager.  Because disable uses this state instead of
2323   * that found in META, we can't seem to cleanly disable/delete tables that
2324   * have been hbck fixed.  When used on a version of HBase that does not have
2325   * the offline ipc call exposed on the master (&lt;0.90.5, &lt;0.92.0) a master
2326   * restart or failover may be required.
2327   */
2328  private void closeRegion(HbckInfo hi) throws IOException, InterruptedException {
2329    if (hi.metaEntry == null && hi.hdfsEntry == null) {
2330      undeployRegions(hi);
2331      return;
2332    }
2333
2334    // get assignment info and hregioninfo from meta.
2335    Get get = new Get(hi.getRegionName());
2336    get.addColumn(HConstants.CATALOG_FAMILY, HConstants.REGIONINFO_QUALIFIER);
2337    get.addColumn(HConstants.CATALOG_FAMILY, HConstants.SERVER_QUALIFIER);
2338    get.addColumn(HConstants.CATALOG_FAMILY, HConstants.STARTCODE_QUALIFIER);
2339    // also get the locations of the replicas to close if the primary region is being closed
2340    if (hi.getReplicaId() == RegionInfo.DEFAULT_REPLICA_ID) {
2341      int numReplicas = admin.getTableDescriptor(hi.getTableName()).getRegionReplication();
2342      for (int i = 0; i < numReplicas; i++) {
2343        get.addColumn(HConstants.CATALOG_FAMILY, MetaTableAccessor.getServerColumn(i));
2344        get.addColumn(HConstants.CATALOG_FAMILY, MetaTableAccessor.getStartCodeColumn(i));
2345      }
2346    }
2347    Result r = meta.get(get);
2348    RegionLocations rl = MetaTableAccessor.getRegionLocations(r);
2349    if (rl == null) {
2350      LOG.warn("Unable to close region " + hi.getRegionNameAsString() +
2351          " since meta does not have handle to reach it");
2352      return;
2353    }
2354    for (HRegionLocation h : rl.getRegionLocations()) {
2355      ServerName serverName = h.getServerName();
2356      if (serverName == null) {
2357        errors.reportError("Unable to close region "
2358            + hi.getRegionNameAsString() +  " because meta does not "
2359            + "have handle to reach it.");
2360        continue;
2361      }
2362      RegionInfo hri = h.getRegionInfo();
2363      if (hri == null) {
2364        LOG.warn("Unable to close region " + hi.getRegionNameAsString()
2365            + " because hbase:meta had invalid or missing "
2366            + HConstants.CATALOG_FAMILY_STR + ":"
2367            + Bytes.toString(HConstants.REGIONINFO_QUALIFIER)
2368            + " qualifier value.");
2369        continue;
2370      }
2371      // close the region -- close files and remove assignment
2372      HBaseFsckRepair.closeRegionSilentlyAndWait(connection, serverName, hri);
2373    }
2374  }
2375
2376  private void tryAssignmentRepair(HbckInfo hbi, String msg) throws IOException,
2377    KeeperException, InterruptedException {
2378    // If we are trying to fix the errors
2379    if (shouldFixAssignments()) {
2380      errors.print(msg);
2381      undeployRegions(hbi);
2382      setShouldRerun();
2383      RegionInfo hri = hbi.getHdfsHRI();
2384      if (hri == null) {
2385        hri = hbi.metaEntry;
2386      }
2387      HBaseFsckRepair.fixUnassigned(admin, hri);
2388      HBaseFsckRepair.waitUntilAssigned(admin, hri);
2389
2390      // also assign replicas if needed (do it only when this call operates on a primary replica)
2391      if (hbi.getReplicaId() != RegionInfo.DEFAULT_REPLICA_ID) return;
2392      int replicationCount = admin.getTableDescriptor(hri.getTable()).getRegionReplication();
2393      for (int i = 1; i < replicationCount; i++) {
2394        hri = RegionReplicaUtil.getRegionInfoForReplica(hri, i);
2395        HbckInfo h = regionInfoMap.get(hri.getEncodedName());
2396        if (h != null) {
2397          undeployRegions(h);
2398          //set skip checks; we undeploy & deploy it; we don't want to evaluate this hbi anymore
2399          //in consistency checks
2400          h.setSkipChecks(true);
2401        }
2402        HBaseFsckRepair.fixUnassigned(admin, hri);
2403        HBaseFsckRepair.waitUntilAssigned(admin, hri);
2404      }
2405
2406    }
2407  }
2408
2409  /**
2410   * Check a single region for consistency and correct deployment.
2411   */
2412  private void checkRegionConsistency(final String key, final HbckInfo hbi)
2413  throws IOException, KeeperException, InterruptedException {
2414
2415    if (hbi.isSkipChecks()) return;
2416    String descriptiveName = hbi.toString();
2417    boolean inMeta = hbi.metaEntry != null;
2418    // In case not checking HDFS, assume the region is on HDFS
2419    boolean inHdfs = !shouldCheckHdfs() || hbi.getHdfsRegionDir() != null;
2420    boolean hasMetaAssignment = inMeta && hbi.metaEntry.regionServer != null;
2421    boolean isDeployed = !hbi.deployedOn.isEmpty();
2422    boolean isMultiplyDeployed = hbi.deployedOn.size() > 1;
2423    boolean deploymentMatchesMeta =
2424      hasMetaAssignment && isDeployed && !isMultiplyDeployed &&
2425      hbi.metaEntry.regionServer.equals(hbi.deployedOn.get(0));
2426    boolean splitParent =
2427        inMeta && hbi.metaEntry.isSplit() && hbi.metaEntry.isOffline();
2428    boolean shouldBeDeployed = inMeta && !isTableDisabled(hbi.metaEntry.getTable());
2429    boolean recentlyModified = inHdfs &&
2430      hbi.getModTime() + timelag > EnvironmentEdgeManager.currentTime();
2431
2432    // ========== First the healthy cases =============
2433    if (hbi.containsOnlyHdfsEdits()) {
2434      return;
2435    }
2436    if (inMeta && inHdfs && isDeployed && deploymentMatchesMeta && shouldBeDeployed) {
2437      return;
2438    } else if (inMeta && inHdfs && !shouldBeDeployed && !isDeployed) {
2439      LOG.info("Region " + descriptiveName + " is in META, and in a disabled " +
2440        "tabled that is not deployed");
2441      return;
2442    } else if (recentlyModified) {
2443      LOG.warn("Region " + descriptiveName + " was recently modified -- skipping");
2444      return;
2445    }
2446    // ========== Cases where the region is not in hbase:meta =============
2447    else if (!inMeta && !inHdfs && !isDeployed) {
2448      // We shouldn't have record of this region at all then!
2449      assert false : "Entry for region with no data";
2450    } else if (!inMeta && !inHdfs && isDeployed) {
2451      errors.reportError(ERROR_CODE.NOT_IN_META_HDFS, "Region "
2452          + descriptiveName + ", key=" + key + ", not on HDFS or in hbase:meta but " +
2453          "deployed on " + Joiner.on(", ").join(hbi.deployedOn));
2454      if (shouldFixAssignments()) {
2455        undeployRegions(hbi);
2456      }
2457
2458    } else if (!inMeta && inHdfs && !isDeployed) {
2459      if (hbi.isMerged()) {
2460        // This region has already been merged, the remaining hdfs file will be
2461        // cleaned by CatalogJanitor later
2462        hbi.setSkipChecks(true);
2463        LOG.info("Region " + descriptiveName
2464            + " got merge recently, its file(s) will be cleaned by CatalogJanitor later");
2465        return;
2466      }
2467      errors.reportError(ERROR_CODE.NOT_IN_META_OR_DEPLOYED, "Region "
2468          + descriptiveName + " on HDFS, but not listed in hbase:meta " +
2469          "or deployed on any region server");
2470      // restore region consistency of an adopted orphan
2471      if (shouldFixMeta()) {
2472        if (!hbi.isHdfsRegioninfoPresent()) {
2473          LOG.error("Region " + hbi.getHdfsHRI() + " could have been repaired"
2474              +  " in table integrity repair phase if -fixHdfsOrphans was" +
2475              " used.");
2476          return;
2477        }
2478
2479        RegionInfo hri = hbi.getHdfsHRI();
2480        TableInfo tableInfo = tablesInfo.get(hri.getTable());
2481
2482        for (RegionInfo region : tableInfo.getRegionsFromMeta()) {
2483          if (Bytes.compareTo(region.getStartKey(), hri.getStartKey()) <= 0
2484              && (region.getEndKey().length == 0 || Bytes.compareTo(region.getEndKey(),
2485                hri.getEndKey()) >= 0)
2486              && Bytes.compareTo(region.getStartKey(), hri.getEndKey()) <= 0) {
2487            if(region.isSplit() || region.isOffline()) continue;
2488            Path regionDir = hbi.getHdfsRegionDir();
2489            FileSystem fs = regionDir.getFileSystem(getConf());
2490            List<Path> familyDirs = FSUtils.getFamilyDirs(fs, regionDir);
2491            for (Path familyDir : familyDirs) {
2492              List<Path> referenceFilePaths = FSUtils.getReferenceFilePaths(fs, familyDir);
2493              for (Path referenceFilePath : referenceFilePaths) {
2494                Path parentRegionDir =
2495                    StoreFileInfo.getReferredToFile(referenceFilePath).getParent().getParent();
2496                if (parentRegionDir.toString().endsWith(region.getEncodedName())) {
2497                  LOG.warn(hri + " start and stop keys are in the range of " + region
2498                      + ". The region might not be cleaned up from hdfs when region " + region
2499                      + " split failed. Hence deleting from hdfs.");
2500                  HRegionFileSystem.deleteRegionFromFileSystem(getConf(), fs,
2501                    regionDir.getParent(), hri);
2502                  return;
2503                }
2504              }
2505            }
2506          }
2507        }
2508        LOG.info("Patching hbase:meta with .regioninfo: " + hbi.getHdfsHRI());
2509        int numReplicas = admin.getTableDescriptor(hbi.getTableName()).getRegionReplication();
2510        HBaseFsckRepair.fixMetaHoleOnlineAndAddReplicas(getConf(), hbi.getHdfsHRI(),
2511            admin.getClusterMetrics(EnumSet.of(Option.LIVE_SERVERS))
2512              .getLiveServerMetrics().keySet(), numReplicas);
2513
2514        tryAssignmentRepair(hbi, "Trying to reassign region...");
2515      }
2516
2517    } else if (!inMeta && inHdfs && isDeployed) {
2518      errors.reportError(ERROR_CODE.NOT_IN_META, "Region " + descriptiveName
2519          + " not in META, but deployed on " + Joiner.on(", ").join(hbi.deployedOn));
2520      debugLsr(hbi.getHdfsRegionDir());
2521      if (hbi.getReplicaId() != RegionInfo.DEFAULT_REPLICA_ID) {
2522        // for replicas, this means that we should undeploy the region (we would have
2523        // gone over the primaries and fixed meta holes in first phase under
2524        // checkAndFixConsistency; we shouldn't get the condition !inMeta at
2525        // this stage unless unwanted replica)
2526        if (shouldFixAssignments()) {
2527          undeployRegionsForHbi(hbi);
2528        }
2529      }
2530      if (shouldFixMeta() && hbi.getReplicaId() == RegionInfo.DEFAULT_REPLICA_ID) {
2531        if (!hbi.isHdfsRegioninfoPresent()) {
2532          LOG.error("This should have been repaired in table integrity repair phase");
2533          return;
2534        }
2535
2536        LOG.info("Patching hbase:meta with with .regioninfo: " + hbi.getHdfsHRI());
2537        int numReplicas = admin.getTableDescriptor(hbi.getTableName()).getRegionReplication();
2538        HBaseFsckRepair.fixMetaHoleOnlineAndAddReplicas(getConf(), hbi.getHdfsHRI(),
2539            admin.getClusterMetrics(EnumSet.of(Option.LIVE_SERVERS))
2540              .getLiveServerMetrics().keySet(), numReplicas);
2541        tryAssignmentRepair(hbi, "Trying to fix unassigned region...");
2542      }
2543
2544    // ========== Cases where the region is in hbase:meta =============
2545    } else if (inMeta && inHdfs && !isDeployed && splitParent) {
2546      // check whether this is an actual error, or just transient state where parent
2547      // is not cleaned
2548      if (hbi.metaEntry.splitA != null && hbi.metaEntry.splitB != null) {
2549        // check that split daughters are there
2550        HbckInfo infoA = this.regionInfoMap.get(hbi.metaEntry.splitA.getEncodedName());
2551        HbckInfo infoB = this.regionInfoMap.get(hbi.metaEntry.splitB.getEncodedName());
2552        if (infoA != null && infoB != null) {
2553          // we already processed or will process daughters. Move on, nothing to see here.
2554          hbi.setSkipChecks(true);
2555          return;
2556        }
2557      }
2558
2559      // For Replica region, we need to do a similar check. If replica is not split successfully,
2560      // error is going to be reported against primary daughter region.
2561      if (hbi.getReplicaId() != RegionInfo.DEFAULT_REPLICA_ID) {
2562        LOG.info("Region " + descriptiveName + " is a split parent in META, in HDFS, "
2563            + "and not deployed on any region server. This may be transient.");
2564        hbi.setSkipChecks(true);
2565        return;
2566      }
2567
2568      errors.reportError(ERROR_CODE.LINGERING_SPLIT_PARENT, "Region "
2569          + descriptiveName + " is a split parent in META, in HDFS, "
2570          + "and not deployed on any region server. This could be transient, "
2571          + "consider to run the catalog janitor first!");
2572      if (shouldFixSplitParents()) {
2573        setShouldRerun();
2574        resetSplitParent(hbi);
2575      }
2576    } else if (inMeta && !inHdfs && !isDeployed) {
2577      errors.reportError(ERROR_CODE.NOT_IN_HDFS_OR_DEPLOYED, "Region "
2578          + descriptiveName + " found in META, but not in HDFS "
2579          + "or deployed on any region server.");
2580      if (shouldFixMeta()) {
2581        deleteMetaRegion(hbi);
2582      }
2583    } else if (inMeta && !inHdfs && isDeployed) {
2584      errors.reportError(ERROR_CODE.NOT_IN_HDFS, "Region " + descriptiveName
2585          + " found in META, but not in HDFS, " +
2586          "and deployed on " + Joiner.on(", ").join(hbi.deployedOn));
2587      // We treat HDFS as ground truth.  Any information in meta is transient
2588      // and equivalent data can be regenerated.  So, lets unassign and remove
2589      // these problems from META.
2590      if (shouldFixAssignments()) {
2591        errors.print("Trying to fix unassigned region...");
2592        undeployRegions(hbi);
2593      }
2594      if (shouldFixMeta()) {
2595        // wait for it to complete
2596        deleteMetaRegion(hbi);
2597      }
2598    } else if (inMeta && inHdfs && !isDeployed && shouldBeDeployed) {
2599      errors.reportError(ERROR_CODE.NOT_DEPLOYED, "Region " + descriptiveName
2600          + " not deployed on any region server.");
2601      tryAssignmentRepair(hbi, "Trying to fix unassigned region...");
2602    } else if (inMeta && inHdfs && isDeployed && !shouldBeDeployed) {
2603      errors.reportError(ERROR_CODE.SHOULD_NOT_BE_DEPLOYED,
2604          "Region " + descriptiveName + " should not be deployed according " +
2605          "to META, but is deployed on " + Joiner.on(", ").join(hbi.deployedOn));
2606      if (shouldFixAssignments()) {
2607        errors.print("Trying to close the region " + descriptiveName);
2608        setShouldRerun();
2609        HBaseFsckRepair.fixMultiAssignment(connection, hbi.metaEntry, hbi.deployedOn);
2610      }
2611    } else if (inMeta && inHdfs && isMultiplyDeployed) {
2612      errors.reportError(ERROR_CODE.MULTI_DEPLOYED, "Region " + descriptiveName
2613          + " is listed in hbase:meta on region server " + hbi.metaEntry.regionServer
2614          + " but is multiply assigned to region servers " +
2615          Joiner.on(", ").join(hbi.deployedOn));
2616      // If we are trying to fix the errors
2617      if (shouldFixAssignments()) {
2618        errors.print("Trying to fix assignment error...");
2619        setShouldRerun();
2620        HBaseFsckRepair.fixMultiAssignment(connection, hbi.metaEntry, hbi.deployedOn);
2621      }
2622    } else if (inMeta && inHdfs && isDeployed && !deploymentMatchesMeta) {
2623      errors.reportError(ERROR_CODE.SERVER_DOES_NOT_MATCH_META, "Region "
2624          + descriptiveName + " listed in hbase:meta on region server " +
2625          hbi.metaEntry.regionServer + " but found on region server " +
2626          hbi.deployedOn.get(0));
2627      // If we are trying to fix the errors
2628      if (shouldFixAssignments()) {
2629        errors.print("Trying to fix assignment error...");
2630        setShouldRerun();
2631        HBaseFsckRepair.fixMultiAssignment(connection, hbi.metaEntry, hbi.deployedOn);
2632        HBaseFsckRepair.waitUntilAssigned(admin, hbi.getHdfsHRI());
2633      }
2634    } else {
2635      errors.reportError(ERROR_CODE.UNKNOWN, "Region " + descriptiveName +
2636          " is in an unforeseen state:" +
2637          " inMeta=" + inMeta +
2638          " inHdfs=" + inHdfs +
2639          " isDeployed=" + isDeployed +
2640          " isMultiplyDeployed=" + isMultiplyDeployed +
2641          " deploymentMatchesMeta=" + deploymentMatchesMeta +
2642          " shouldBeDeployed=" + shouldBeDeployed);
2643    }
2644  }
2645
2646  /**
2647   * Checks tables integrity. Goes over all regions and scans the tables.
2648   * Collects all the pieces for each table and checks if there are missing,
2649   * repeated or overlapping ones.
2650   * @throws IOException
2651   */
2652  SortedMap<TableName, TableInfo> checkIntegrity() throws IOException {
2653    tablesInfo = new TreeMap<>();
2654    LOG.debug("There are " + regionInfoMap.size() + " region info entries");
2655    for (HbckInfo hbi : regionInfoMap.values()) {
2656      // Check only valid, working regions
2657      if (hbi.metaEntry == null) {
2658        // this assumes that consistency check has run loadMetaEntry
2659        Path p = hbi.getHdfsRegionDir();
2660        if (p == null) {
2661          errors.report("No regioninfo in Meta or HDFS. " + hbi);
2662        }
2663
2664        // TODO test.
2665        continue;
2666      }
2667      if (hbi.metaEntry.regionServer == null) {
2668        errors.detail("Skipping region because no region server: " + hbi);
2669        continue;
2670      }
2671      if (hbi.metaEntry.isOffline()) {
2672        errors.detail("Skipping region because it is offline: " + hbi);
2673        continue;
2674      }
2675      if (hbi.containsOnlyHdfsEdits()) {
2676        errors.detail("Skipping region because it only contains edits" + hbi);
2677        continue;
2678      }
2679
2680      // Missing regionDir or over-deployment is checked elsewhere. Include
2681      // these cases in modTInfo, so we can evaluate those regions as part of
2682      // the region chain in META
2683      //if (hbi.foundRegionDir == null) continue;
2684      //if (hbi.deployedOn.size() != 1) continue;
2685      if (hbi.deployedOn.isEmpty()) continue;
2686
2687      // We should be safe here
2688      TableName tableName = hbi.metaEntry.getTable();
2689      TableInfo modTInfo = tablesInfo.get(tableName);
2690      if (modTInfo == null) {
2691        modTInfo = new TableInfo(tableName);
2692      }
2693      for (ServerName server : hbi.deployedOn) {
2694        modTInfo.addServer(server);
2695      }
2696
2697      if (!hbi.isSkipChecks()) {
2698        modTInfo.addRegionInfo(hbi);
2699      }
2700
2701      tablesInfo.put(tableName, modTInfo);
2702    }
2703
2704    loadTableInfosForTablesWithNoRegion();
2705
2706    logParallelMerge();
2707    for (TableInfo tInfo : tablesInfo.values()) {
2708      TableIntegrityErrorHandler handler = tInfo.new IntegrityFixSuggester(tInfo, errors);
2709      if (!tInfo.checkRegionChain(handler)) {
2710        errors.report("Found inconsistency in table " + tInfo.getName());
2711      }
2712    }
2713    return tablesInfo;
2714  }
2715
2716  /** Loads table info's for tables that may not have been included, since there are no
2717   * regions reported for the table, but table dir is there in hdfs
2718   */
2719  private void loadTableInfosForTablesWithNoRegion() throws IOException {
2720    Map<String, TableDescriptor> allTables = new FSTableDescriptors(getConf()).getAll();
2721    for (TableDescriptor htd : allTables.values()) {
2722      if (checkMetaOnly && !htd.isMetaTable()) {
2723        continue;
2724      }
2725
2726      TableName tableName = htd.getTableName();
2727      if (isTableIncluded(tableName) && !tablesInfo.containsKey(tableName)) {
2728        TableInfo tableInfo = new TableInfo(tableName);
2729        tableInfo.htds.add(htd);
2730        tablesInfo.put(htd.getTableName(), tableInfo);
2731      }
2732    }
2733  }
2734
2735  /**
2736   * Merge hdfs data by moving from contained HbckInfo into targetRegionDir.
2737   * @return number of file move fixes done to merge regions.
2738   */
2739  public int mergeRegionDirs(Path targetRegionDir, HbckInfo contained) throws IOException {
2740    int fileMoves = 0;
2741    String thread = Thread.currentThread().getName();
2742    LOG.debug("[" + thread + "] Contained region dir after close and pause");
2743    debugLsr(contained.getHdfsRegionDir());
2744
2745    // rename the contained into the container.
2746    FileSystem fs = targetRegionDir.getFileSystem(getConf());
2747    FileStatus[] dirs = null;
2748    try {
2749      dirs = fs.listStatus(contained.getHdfsRegionDir());
2750    } catch (FileNotFoundException fnfe) {
2751      // region we are attempting to merge in is not present!  Since this is a merge, there is
2752      // no harm skipping this region if it does not exist.
2753      if (!fs.exists(contained.getHdfsRegionDir())) {
2754        LOG.warn("[" + thread + "] HDFS region dir " + contained.getHdfsRegionDir()
2755            + " is missing. Assuming already sidelined or moved.");
2756      } else {
2757        sidelineRegionDir(fs, contained);
2758      }
2759      return fileMoves;
2760    }
2761
2762    if (dirs == null) {
2763      if (!fs.exists(contained.getHdfsRegionDir())) {
2764        LOG.warn("[" + thread + "] HDFS region dir " + contained.getHdfsRegionDir()
2765            + " already sidelined.");
2766      } else {
2767        sidelineRegionDir(fs, contained);
2768      }
2769      return fileMoves;
2770    }
2771
2772    for (FileStatus cf : dirs) {
2773      Path src = cf.getPath();
2774      Path dst =  new Path(targetRegionDir, src.getName());
2775
2776      if (src.getName().equals(HRegionFileSystem.REGION_INFO_FILE)) {
2777        // do not copy the old .regioninfo file.
2778        continue;
2779      }
2780
2781      if (src.getName().equals(HConstants.HREGION_OLDLOGDIR_NAME)) {
2782        // do not copy the .oldlogs files
2783        continue;
2784      }
2785
2786      LOG.info("[" + thread + "] Moving files from " + src + " into containing region " + dst);
2787      // FileSystem.rename is inconsistent with directories -- if the
2788      // dst (foo/a) exists and is a dir, and the src (foo/b) is a dir,
2789      // it moves the src into the dst dir resulting in (foo/a/b).  If
2790      // the dst does not exist, and the src a dir, src becomes dst. (foo/b)
2791      for (FileStatus hfile : fs.listStatus(src)) {
2792        boolean success = fs.rename(hfile.getPath(), dst);
2793        if (success) {
2794          fileMoves++;
2795        }
2796      }
2797      LOG.debug("[" + thread + "] Sideline directory contents:");
2798      debugLsr(targetRegionDir);
2799    }
2800
2801    // if all success.
2802    sidelineRegionDir(fs, contained);
2803    LOG.info("[" + thread + "] Sidelined region dir "+ contained.getHdfsRegionDir() + " into " +
2804        getSidelineDir());
2805    debugLsr(contained.getHdfsRegionDir());
2806
2807    return fileMoves;
2808  }
2809
2810
2811  static class WorkItemOverlapMerge implements Callable<Void> {
2812    private TableIntegrityErrorHandler handler;
2813    Collection<HbckInfo> overlapgroup;
2814
2815    WorkItemOverlapMerge(Collection<HbckInfo> overlapgroup, TableIntegrityErrorHandler handler) {
2816      this.handler = handler;
2817      this.overlapgroup = overlapgroup;
2818    }
2819
2820    @Override
2821    public Void call() throws Exception {
2822      handler.handleOverlapGroup(overlapgroup);
2823      return null;
2824    }
2825  };
2826
2827
2828  /**
2829   * Maintain information about a particular table.
2830   */
2831  public class TableInfo {
2832    TableName tableName;
2833    TreeSet <ServerName> deployedOn;
2834
2835    // backwards regions
2836    final List<HbckInfo> backwards = new ArrayList<>();
2837
2838    // sidelined big overlapped regions
2839    final Map<Path, HbckInfo> sidelinedRegions = new HashMap<>();
2840
2841    // region split calculator
2842    final RegionSplitCalculator<HbckInfo> sc = new RegionSplitCalculator<>(cmp);
2843
2844    // Histogram of different TableDescriptors found.  Ideally there is only one!
2845    final Set<TableDescriptor> htds = new HashSet<>();
2846
2847    // key = start split, values = set of splits in problem group
2848    final Multimap<byte[], HbckInfo> overlapGroups =
2849      TreeMultimap.create(RegionSplitCalculator.BYTES_COMPARATOR, cmp);
2850
2851    // list of regions derived from meta entries.
2852    private ImmutableList<RegionInfo> regionsFromMeta = null;
2853
2854    TableInfo(TableName name) {
2855      this.tableName = name;
2856      deployedOn = new TreeSet <>();
2857    }
2858
2859    /**
2860     * @return descriptor common to all regions.  null if are none or multiple!
2861     */
2862    private TableDescriptor getHTD() {
2863      if (htds.size() == 1) {
2864        return (TableDescriptor)htds.toArray()[0];
2865      } else {
2866        LOG.error("None/Multiple table descriptors found for table '"
2867          + tableName + "' regions: " + htds);
2868      }
2869      return null;
2870    }
2871
2872    public void addRegionInfo(HbckInfo hir) {
2873      if (Bytes.equals(hir.getEndKey(), HConstants.EMPTY_END_ROW)) {
2874        // end key is absolute end key, just add it.
2875        // ignore replicas other than primary for these checks
2876        if (hir.getReplicaId() == RegionInfo.DEFAULT_REPLICA_ID) sc.add(hir);
2877        return;
2878      }
2879
2880      // if not the absolute end key, check for cycle
2881      if (Bytes.compareTo(hir.getStartKey(), hir.getEndKey()) > 0) {
2882        errors.reportError(
2883            ERROR_CODE.REGION_CYCLE,
2884            String.format("The endkey for this region comes before the "
2885                + "startkey, startkey=%s, endkey=%s",
2886                Bytes.toStringBinary(hir.getStartKey()),
2887                Bytes.toStringBinary(hir.getEndKey())), this, hir);
2888        backwards.add(hir);
2889        return;
2890      }
2891
2892      // main case, add to split calculator
2893      // ignore replicas other than primary for these checks
2894      if (hir.getReplicaId() == RegionInfo.DEFAULT_REPLICA_ID) sc.add(hir);
2895    }
2896
2897    public void addServer(ServerName server) {
2898      this.deployedOn.add(server);
2899    }
2900
2901    public TableName getName() {
2902      return tableName;
2903    }
2904
2905    public int getNumRegions() {
2906      return sc.getStarts().size() + backwards.size();
2907    }
2908
2909    public synchronized ImmutableList<RegionInfo> getRegionsFromMeta() {
2910      // lazy loaded, synchronized to ensure a single load
2911      if (regionsFromMeta == null) {
2912        List<RegionInfo> regions = new ArrayList<>();
2913        for (HbckInfo h : HBaseFsck.this.regionInfoMap.values()) {
2914          if (tableName.equals(h.getTableName())) {
2915            if (h.metaEntry != null) {
2916              regions.add(h.metaEntry);
2917            }
2918          }
2919        }
2920        regionsFromMeta = Ordering.from(RegionInfo.COMPARATOR).immutableSortedCopy(regions);
2921      }
2922
2923      return regionsFromMeta;
2924    }
2925
2926    private class IntegrityFixSuggester extends TableIntegrityErrorHandlerImpl {
2927      ErrorReporter errors;
2928
2929      IntegrityFixSuggester(TableInfo ti, ErrorReporter errors) {
2930        this.errors = errors;
2931        setTableInfo(ti);
2932      }
2933
2934      @Override
2935      public void handleRegionStartKeyNotEmpty(HbckInfo hi) throws IOException{
2936        errors.reportError(ERROR_CODE.FIRST_REGION_STARTKEY_NOT_EMPTY,
2937            "First region should start with an empty key.  You need to "
2938            + " create a new region and regioninfo in HDFS to plug the hole.",
2939            getTableInfo(), hi);
2940      }
2941
2942      @Override
2943      public void handleRegionEndKeyNotEmpty(byte[] curEndKey) throws IOException {
2944        errors.reportError(ERROR_CODE.LAST_REGION_ENDKEY_NOT_EMPTY,
2945            "Last region should end with an empty key. You need to "
2946                + "create a new region and regioninfo in HDFS to plug the hole.", getTableInfo());
2947      }
2948
2949      @Override
2950      public void handleDegenerateRegion(HbckInfo hi) throws IOException{
2951        errors.reportError(ERROR_CODE.DEGENERATE_REGION,
2952            "Region has the same start and end key.", getTableInfo(), hi);
2953      }
2954
2955      @Override
2956      public void handleDuplicateStartKeys(HbckInfo r1, HbckInfo r2) throws IOException{
2957        byte[] key = r1.getStartKey();
2958        // dup start key
2959        errors.reportError(ERROR_CODE.DUPE_STARTKEYS,
2960            "Multiple regions have the same startkey: "
2961            + Bytes.toStringBinary(key), getTableInfo(), r1);
2962        errors.reportError(ERROR_CODE.DUPE_STARTKEYS,
2963            "Multiple regions have the same startkey: "
2964            + Bytes.toStringBinary(key), getTableInfo(), r2);
2965      }
2966
2967      @Override
2968      public void handleSplit(HbckInfo r1, HbckInfo r2) throws IOException{
2969        byte[] key = r1.getStartKey();
2970        // dup start key
2971        errors.reportError(ERROR_CODE.DUPE_ENDKEYS,
2972          "Multiple regions have the same regionID: "
2973            + Bytes.toStringBinary(key), getTableInfo(), r1);
2974        errors.reportError(ERROR_CODE.DUPE_ENDKEYS,
2975          "Multiple regions have the same regionID: "
2976            + Bytes.toStringBinary(key), getTableInfo(), r2);
2977      }
2978
2979      @Override
2980      public void handleOverlapInRegionChain(HbckInfo hi1, HbckInfo hi2) throws IOException{
2981        errors.reportError(ERROR_CODE.OVERLAP_IN_REGION_CHAIN,
2982            "There is an overlap in the region chain.",
2983            getTableInfo(), hi1, hi2);
2984      }
2985
2986      @Override
2987      public void handleHoleInRegionChain(byte[] holeStart, byte[] holeStop) throws IOException{
2988        errors.reportError(
2989            ERROR_CODE.HOLE_IN_REGION_CHAIN,
2990            "There is a hole in the region chain between "
2991                + Bytes.toStringBinary(holeStart) + " and "
2992                + Bytes.toStringBinary(holeStop)
2993                + ".  You need to create a new .regioninfo and region "
2994                + "dir in hdfs to plug the hole.");
2995      }
2996    };
2997
2998    /**
2999     * This handler fixes integrity errors from hdfs information.  There are
3000     * basically three classes of integrity problems 1) holes, 2) overlaps, and
3001     * 3) invalid regions.
3002     *
3003     * This class overrides methods that fix holes and the overlap group case.
3004     * Individual cases of particular overlaps are handled by the general
3005     * overlap group merge repair case.
3006     *
3007     * If hbase is online, this forces regions offline before doing merge
3008     * operations.
3009     */
3010    private class HDFSIntegrityFixer extends IntegrityFixSuggester {
3011      Configuration conf;
3012
3013      boolean fixOverlaps = true;
3014
3015      HDFSIntegrityFixer(TableInfo ti, ErrorReporter errors, Configuration conf,
3016          boolean fixHoles, boolean fixOverlaps) {
3017        super(ti, errors);
3018        this.conf = conf;
3019        this.fixOverlaps = fixOverlaps;
3020        // TODO properly use fixHoles
3021      }
3022
3023      /**
3024       * This is a special case hole -- when the first region of a table is
3025       * missing from META, HBase doesn't acknowledge the existance of the
3026       * table.
3027       */
3028      @Override
3029      public void handleRegionStartKeyNotEmpty(HbckInfo next) throws IOException {
3030        errors.reportError(ERROR_CODE.FIRST_REGION_STARTKEY_NOT_EMPTY,
3031            "First region should start with an empty key.  Creating a new " +
3032            "region and regioninfo in HDFS to plug the hole.",
3033            getTableInfo(), next);
3034        TableDescriptor htd = getTableInfo().getHTD();
3035        // from special EMPTY_START_ROW to next region's startKey
3036        RegionInfo newRegion = RegionInfoBuilder.newBuilder(htd.getTableName())
3037            .setStartKey(HConstants.EMPTY_START_ROW)
3038            .setEndKey(next.getStartKey())
3039            .build();
3040
3041        // TODO test
3042        HRegion region = HBaseFsckRepair.createHDFSRegionDir(conf, newRegion, htd);
3043        LOG.info("Table region start key was not empty.  Created new empty region: "
3044            + newRegion + " " +region);
3045        fixes++;
3046      }
3047
3048      @Override
3049      public void handleRegionEndKeyNotEmpty(byte[] curEndKey) throws IOException {
3050        errors.reportError(ERROR_CODE.LAST_REGION_ENDKEY_NOT_EMPTY,
3051            "Last region should end with an empty key.  Creating a new "
3052                + "region and regioninfo in HDFS to plug the hole.", getTableInfo());
3053        TableDescriptor htd = getTableInfo().getHTD();
3054        // from curEndKey to EMPTY_START_ROW
3055        RegionInfo newRegion = RegionInfoBuilder.newBuilder(htd.getTableName())
3056            .setStartKey(curEndKey)
3057            .setEndKey(HConstants.EMPTY_START_ROW)
3058            .build();
3059
3060        HRegion region = HBaseFsckRepair.createHDFSRegionDir(conf, newRegion, htd);
3061        LOG.info("Table region end key was not empty.  Created new empty region: " + newRegion
3062            + " " + region);
3063        fixes++;
3064      }
3065
3066      /**
3067       * There is a hole in the hdfs regions that violates the table integrity
3068       * rules.  Create a new empty region that patches the hole.
3069       */
3070      @Override
3071      public void handleHoleInRegionChain(byte[] holeStartKey, byte[] holeStopKey) throws IOException {
3072        errors.reportError(
3073            ERROR_CODE.HOLE_IN_REGION_CHAIN,
3074            "There is a hole in the region chain between "
3075                + Bytes.toStringBinary(holeStartKey) + " and "
3076                + Bytes.toStringBinary(holeStopKey)
3077                + ".  Creating a new regioninfo and region "
3078                + "dir in hdfs to plug the hole.");
3079        TableDescriptor htd = getTableInfo().getHTD();
3080        RegionInfo newRegion = RegionInfoBuilder.newBuilder(htd.getTableName())
3081            .setStartKey(holeStartKey)
3082            .setEndKey(holeStopKey)
3083            .build();
3084        HRegion region = HBaseFsckRepair.createHDFSRegionDir(conf, newRegion, htd);
3085        LOG.info("Plugged hole by creating new empty region: "+ newRegion + " " +region);
3086        fixes++;
3087      }
3088
3089      /**
3090       * This takes set of overlapping regions and merges them into a single
3091       * region.  This covers cases like degenerate regions, shared start key,
3092       * general overlaps, duplicate ranges, and partial overlapping regions.
3093       *
3094       * Cases:
3095       * - Clean regions that overlap
3096       * - Only .oldlogs regions (can't find start/stop range, or figure out)
3097       *
3098       * This is basically threadsafe, except for the fixer increment in mergeOverlaps.
3099       */
3100      @Override
3101      public void handleOverlapGroup(Collection<HbckInfo> overlap)
3102          throws IOException {
3103        Preconditions.checkNotNull(overlap);
3104        Preconditions.checkArgument(overlap.size() >0);
3105
3106        if (!this.fixOverlaps) {
3107          LOG.warn("Not attempting to repair overlaps.");
3108          return;
3109        }
3110
3111        if (overlap.size() > maxMerge) {
3112          LOG.warn("Overlap group has " + overlap.size() + " overlapping " +
3113            "regions which is greater than " + maxMerge + ", the max number of regions to merge");
3114          if (sidelineBigOverlaps) {
3115            // we only sideline big overlapped groups that exceeds the max number of regions to merge
3116            sidelineBigOverlaps(overlap);
3117          }
3118          return;
3119        }
3120        if (shouldRemoveParents()) {
3121          removeParentsAndFixSplits(overlap);
3122        }
3123        mergeOverlaps(overlap);
3124      }
3125
3126      void removeParentsAndFixSplits(Collection<HbckInfo> overlap) throws IOException {
3127        Pair<byte[], byte[]> range = null;
3128        HbckInfo parent = null;
3129        HbckInfo daughterA = null;
3130        HbckInfo daughterB = null;
3131        Collection<HbckInfo> daughters = new ArrayList<HbckInfo>(overlap);
3132
3133        String thread = Thread.currentThread().getName();
3134        LOG.info("== [" + thread + "] Attempting fix splits in overlap state.");
3135
3136        // we only can handle a single split per group at the time
3137        if (overlap.size() > 3) {
3138          LOG.info("Too many overlaps were found on this group, falling back to regular merge.");
3139          return;
3140        }
3141
3142        for (HbckInfo hi : overlap) {
3143          if (range == null) {
3144            range = new Pair<byte[], byte[]>(hi.getStartKey(), hi.getEndKey());
3145          } else {
3146            if (RegionSplitCalculator.BYTES_COMPARATOR
3147              .compare(hi.getStartKey(), range.getFirst()) < 0) {
3148              range.setFirst(hi.getStartKey());
3149            }
3150            if (RegionSplitCalculator.BYTES_COMPARATOR
3151              .compare(hi.getEndKey(), range.getSecond()) > 0) {
3152              range.setSecond(hi.getEndKey());
3153            }
3154          }
3155        }
3156
3157        LOG.info("This group range is [" + Bytes.toStringBinary(range.getFirst()) + ", "
3158          + Bytes.toStringBinary(range.getSecond()) + "]");
3159
3160        // attempt to find a possible parent for the edge case of a split
3161        for (HbckInfo hi : overlap) {
3162          if (Bytes.compareTo(hi.getHdfsHRI().getStartKey(), range.getFirst()) == 0
3163            && Bytes.compareTo(hi.getHdfsHRI().getEndKey(), range.getSecond()) == 0) {
3164            LOG.info("This is a parent for this group: " + hi.toString());
3165            parent = hi;
3166          }
3167        }
3168
3169        // Remove parent regions from daughters collection
3170        if (parent != null) {
3171          daughters.remove(parent);
3172        }
3173
3174        // Lets verify that daughters share the regionID at split time and they
3175        // were created after the parent
3176        for (HbckInfo hi : daughters) {
3177          if (Bytes.compareTo(hi.getHdfsHRI().getStartKey(), range.getFirst()) == 0) {
3178            if (parent.getHdfsHRI().getRegionId() < hi.getHdfsHRI().getRegionId()) {
3179              daughterA = hi;
3180            }
3181          }
3182          if (Bytes.compareTo(hi.getHdfsHRI().getEndKey(), range.getSecond()) == 0) {
3183            if (parent.getHdfsHRI().getRegionId() < hi.getHdfsHRI().getRegionId()) {
3184              daughterB = hi;
3185            }
3186          }
3187        }
3188
3189        // daughters must share the same regionID and we should have a parent too
3190        if (daughterA.getHdfsHRI().getRegionId() != daughterB.getHdfsHRI().getRegionId() || parent == null)
3191          return;
3192
3193        FileSystem fs = FileSystem.get(conf);
3194        LOG.info("Found parent: " + parent.getRegionNameAsString());
3195        LOG.info("Found potential daughter a: " + daughterA.getRegionNameAsString());
3196        LOG.info("Found potential daughter b: " + daughterB.getRegionNameAsString());
3197        LOG.info("Trying to fix parent in overlap by removing the parent.");
3198        try {
3199          closeRegion(parent);
3200        } catch (IOException ioe) {
3201          LOG.warn("Parent region could not be closed, continuing with regular merge...", ioe);
3202          return;
3203        } catch (InterruptedException ie) {
3204          LOG.warn("Parent region could not be closed, continuing with regular merge...", ie);
3205          return;
3206        }
3207
3208        try {
3209          offline(parent.getRegionName());
3210        } catch (IOException ioe) {
3211          LOG.warn("Unable to offline parent region: " + parent.getRegionNameAsString()
3212            + ".  Just continuing with regular merge... ", ioe);
3213          return;
3214        }
3215
3216        try {
3217          HBaseFsckRepair.removeParentInMeta(conf, parent.getHdfsHRI());
3218        } catch (IOException ioe) {
3219          LOG.warn("Unable to remove parent region in META: " + parent.getRegionNameAsString()
3220            + ".  Just continuing with regular merge... ", ioe);
3221          return;
3222        }
3223
3224        sidelineRegionDir(fs, parent);
3225        LOG.info("[" + thread + "] Sidelined parent region dir "+ parent.getHdfsRegionDir() + " into " +
3226          getSidelineDir());
3227        debugLsr(parent.getHdfsRegionDir());
3228
3229        // Make sure we don't have the parents and daughters around
3230        overlap.remove(parent);
3231        overlap.remove(daughterA);
3232        overlap.remove(daughterB);
3233
3234        LOG.info("Done fixing split.");
3235
3236      }
3237
3238      void mergeOverlaps(Collection<HbckInfo> overlap)
3239          throws IOException {
3240        String thread = Thread.currentThread().getName();
3241        LOG.info("== [" + thread + "] Merging regions into one region: "
3242          + Joiner.on(",").join(overlap));
3243        // get the min / max range and close all concerned regions
3244        Pair<byte[], byte[]> range = null;
3245        for (HbckInfo hi : overlap) {
3246          if (range == null) {
3247            range = new Pair<>(hi.getStartKey(), hi.getEndKey());
3248          } else {
3249            if (RegionSplitCalculator.BYTES_COMPARATOR
3250                .compare(hi.getStartKey(), range.getFirst()) < 0) {
3251              range.setFirst(hi.getStartKey());
3252            }
3253            if (RegionSplitCalculator.BYTES_COMPARATOR
3254                .compare(hi.getEndKey(), range.getSecond()) > 0) {
3255              range.setSecond(hi.getEndKey());
3256            }
3257          }
3258          // need to close files so delete can happen.
3259          LOG.debug("[" + thread + "] Closing region before moving data around: " +  hi);
3260          LOG.debug("[" + thread + "] Contained region dir before close");
3261          debugLsr(hi.getHdfsRegionDir());
3262          try {
3263            LOG.info("[" + thread + "] Closing region: " + hi);
3264            closeRegion(hi);
3265          } catch (IOException ioe) {
3266            LOG.warn("[" + thread + "] Was unable to close region " + hi
3267              + ".  Just continuing... ", ioe);
3268          } catch (InterruptedException e) {
3269            LOG.warn("[" + thread + "] Was unable to close region " + hi
3270              + ".  Just continuing... ", e);
3271          }
3272
3273          try {
3274            LOG.info("[" + thread + "] Offlining region: " + hi);
3275            offline(hi.getRegionName());
3276          } catch (IOException ioe) {
3277            LOG.warn("[" + thread + "] Unable to offline region from master: " + hi
3278              + ".  Just continuing... ", ioe);
3279          }
3280        }
3281
3282        // create new empty container region.
3283        TableDescriptor htd = getTableInfo().getHTD();
3284        // from start key to end Key
3285        RegionInfo newRegion = RegionInfoBuilder.newBuilder(htd.getTableName())
3286            .setStartKey(range.getFirst())
3287            .setEndKey(range.getSecond())
3288            .build();
3289        HRegion region = HBaseFsckRepair.createHDFSRegionDir(conf, newRegion, htd);
3290        LOG.info("[" + thread + "] Created new empty container region: " +
3291            newRegion + " to contain regions: " + Joiner.on(",").join(overlap));
3292        debugLsr(region.getRegionFileSystem().getRegionDir());
3293
3294        // all target regions are closed, should be able to safely cleanup.
3295        boolean didFix= false;
3296        Path target = region.getRegionFileSystem().getRegionDir();
3297        for (HbckInfo contained : overlap) {
3298          LOG.info("[" + thread + "] Merging " + contained  + " into " + target );
3299          int merges = mergeRegionDirs(target, contained);
3300          if (merges > 0) {
3301            didFix = true;
3302          }
3303        }
3304        if (didFix) {
3305          fixes++;
3306        }
3307      }
3308
3309      /**
3310       * Sideline some regions in a big overlap group so that it
3311       * will have fewer regions, and it is easier to merge them later on.
3312       *
3313       * @param bigOverlap the overlapped group with regions more than maxMerge
3314       * @throws IOException
3315       */
3316      void sidelineBigOverlaps(
3317          Collection<HbckInfo> bigOverlap) throws IOException {
3318        int overlapsToSideline = bigOverlap.size() - maxMerge;
3319        if (overlapsToSideline > maxOverlapsToSideline) {
3320          overlapsToSideline = maxOverlapsToSideline;
3321        }
3322        List<HbckInfo> regionsToSideline =
3323          RegionSplitCalculator.findBigRanges(bigOverlap, overlapsToSideline);
3324        FileSystem fs = FileSystem.get(conf);
3325        for (HbckInfo regionToSideline: regionsToSideline) {
3326          try {
3327            LOG.info("Closing region: " + regionToSideline);
3328            closeRegion(regionToSideline);
3329          } catch (IOException ioe) {
3330            LOG.warn("Was unable to close region " + regionToSideline
3331              + ".  Just continuing... ", ioe);
3332          } catch (InterruptedException e) {
3333            LOG.warn("Was unable to close region " + regionToSideline
3334              + ".  Just continuing... ", e);
3335          }
3336
3337          try {
3338            LOG.info("Offlining region: " + regionToSideline);
3339            offline(regionToSideline.getRegionName());
3340          } catch (IOException ioe) {
3341            LOG.warn("Unable to offline region from master: " + regionToSideline
3342              + ".  Just continuing... ", ioe);
3343          }
3344
3345          LOG.info("Before sideline big overlapped region: " + regionToSideline.toString());
3346          Path sidelineRegionDir = sidelineRegionDir(fs, TO_BE_LOADED, regionToSideline);
3347          if (sidelineRegionDir != null) {
3348            sidelinedRegions.put(sidelineRegionDir, regionToSideline);
3349            LOG.info("After sidelined big overlapped region: "
3350              + regionToSideline.getRegionNameAsString()
3351              + " to " + sidelineRegionDir.toString());
3352            fixes++;
3353          }
3354        }
3355      }
3356    }
3357
3358    /**
3359     * Check the region chain (from META) of this table.  We are looking for
3360     * holes, overlaps, and cycles.
3361     * @return false if there are errors
3362     * @throws IOException
3363     */
3364    public boolean checkRegionChain(TableIntegrityErrorHandler handler) throws IOException {
3365      // When table is disabled no need to check for the region chain. Some of the regions
3366      // accidently if deployed, this below code might report some issues like missing start
3367      // or end regions or region hole in chain and may try to fix which is unwanted.
3368      if (isTableDisabled(this.tableName)) {
3369        return true;
3370      }
3371      int originalErrorsCount = errors.getErrorList().size();
3372      Multimap<byte[], HbckInfo> regions = sc.calcCoverage();
3373      SortedSet<byte[]> splits = sc.getSplits();
3374
3375      byte[] prevKey = null;
3376      byte[] problemKey = null;
3377
3378      if (splits.isEmpty()) {
3379        // no region for this table
3380        handler.handleHoleInRegionChain(HConstants.EMPTY_START_ROW, HConstants.EMPTY_END_ROW);
3381      }
3382
3383      for (byte[] key : splits) {
3384        Collection<HbckInfo> ranges = regions.get(key);
3385        if (prevKey == null && !Bytes.equals(key, HConstants.EMPTY_BYTE_ARRAY)) {
3386          for (HbckInfo rng : ranges) {
3387            handler.handleRegionStartKeyNotEmpty(rng);
3388          }
3389        }
3390
3391        // check for degenerate ranges
3392        for (HbckInfo rng : ranges) {
3393          // special endkey case converts '' to null
3394          byte[] endKey = rng.getEndKey();
3395          endKey = (endKey.length == 0) ? null : endKey;
3396          if (Bytes.equals(rng.getStartKey(),endKey)) {
3397            handler.handleDegenerateRegion(rng);
3398          }
3399        }
3400
3401        if (ranges.size() == 1) {
3402          // this split key is ok -- no overlap, not a hole.
3403          if (problemKey != null) {
3404            LOG.warn("reached end of problem group: " + Bytes.toStringBinary(key));
3405          }
3406          problemKey = null; // fell through, no more problem.
3407        } else if (ranges.size() > 1) {
3408          // set the new problem key group name, if already have problem key, just
3409          // keep using it.
3410          if (problemKey == null) {
3411            // only for overlap regions.
3412            LOG.warn("Naming new problem group: " + Bytes.toStringBinary(key));
3413            problemKey = key;
3414          }
3415          overlapGroups.putAll(problemKey, ranges);
3416
3417          // record errors
3418          ArrayList<HbckInfo> subRange = new ArrayList<>(ranges);
3419          //  this dumb and n^2 but this shouldn't happen often
3420          for (HbckInfo r1 : ranges) {
3421            if (r1.getReplicaId() != RegionInfo.DEFAULT_REPLICA_ID) continue;
3422            subRange.remove(r1);
3423            for (HbckInfo r2 : subRange) {
3424              if (r2.getReplicaId() != RegionInfo.DEFAULT_REPLICA_ID) continue;
3425              // general case of same start key
3426              if (Bytes.compareTo(r1.getStartKey(), r2.getStartKey())==0) {
3427                handler.handleDuplicateStartKeys(r1,r2);
3428              } else if (Bytes.compareTo(r1.getEndKey(), r2.getStartKey())==0 &&
3429                r1.getHdfsHRI().getRegionId() == r2.getHdfsHRI().getRegionId()) {
3430                LOG.info("this is a split, log to splits");
3431                handler.handleSplit(r1, r2);
3432              } else {
3433                // overlap
3434                handler.handleOverlapInRegionChain(r1, r2);
3435              }
3436            }
3437          }
3438
3439        } else if (ranges.isEmpty()) {
3440          if (problemKey != null) {
3441            LOG.warn("reached end of problem group: " + Bytes.toStringBinary(key));
3442          }
3443          problemKey = null;
3444
3445          byte[] holeStopKey = sc.getSplits().higher(key);
3446          // if higher key is null we reached the top.
3447          if (holeStopKey != null) {
3448            // hole
3449            handler.handleHoleInRegionChain(key, holeStopKey);
3450          }
3451        }
3452        prevKey = key;
3453      }
3454
3455      // When the last region of a table is proper and having an empty end key, 'prevKey'
3456      // will be null.
3457      if (prevKey != null) {
3458        handler.handleRegionEndKeyNotEmpty(prevKey);
3459      }
3460
3461      // TODO fold this into the TableIntegrityHandler
3462      if (getConf().getBoolean("hbasefsck.overlap.merge.parallel", true)) {
3463        boolean ok = handleOverlapsParallel(handler, prevKey);
3464        if (!ok) {
3465          return false;
3466        }
3467      } else {
3468        for (Collection<HbckInfo> overlap : overlapGroups.asMap().values()) {
3469          handler.handleOverlapGroup(overlap);
3470        }
3471      }
3472
3473      if (details) {
3474        // do full region split map dump
3475        errors.print("---- Table '"  +  this.tableName
3476            + "': region split map");
3477        dump(splits, regions);
3478        errors.print("---- Table '"  +  this.tableName
3479            + "': overlap groups");
3480        dumpOverlapProblems(overlapGroups);
3481        errors.print("There are " + overlapGroups.keySet().size()
3482            + " overlap groups with " + overlapGroups.size()
3483            + " overlapping regions");
3484      }
3485      if (!sidelinedRegions.isEmpty()) {
3486        LOG.warn("Sidelined big overlapped regions, please bulk load them!");
3487        errors.print("---- Table '"  +  this.tableName
3488            + "': sidelined big overlapped regions");
3489        dumpSidelinedRegions(sidelinedRegions);
3490      }
3491      return errors.getErrorList().size() == originalErrorsCount;
3492    }
3493
3494    private boolean handleOverlapsParallel(TableIntegrityErrorHandler handler, byte[] prevKey)
3495        throws IOException {
3496      // we parallelize overlap handler for the case we have lots of groups to fix.  We can
3497      // safely assume each group is independent.
3498      List<WorkItemOverlapMerge> merges = new ArrayList<>(overlapGroups.size());
3499      List<Future<Void>> rets;
3500      for (Collection<HbckInfo> overlap : overlapGroups.asMap().values()) {
3501        //
3502        merges.add(new WorkItemOverlapMerge(overlap, handler));
3503      }
3504      try {
3505        rets = executor.invokeAll(merges);
3506      } catch (InterruptedException e) {
3507        LOG.error("Overlap merges were interrupted", e);
3508        return false;
3509      }
3510      for(int i=0; i<merges.size(); i++) {
3511        WorkItemOverlapMerge work = merges.get(i);
3512        Future<Void> f = rets.get(i);
3513        try {
3514          f.get();
3515        } catch(ExecutionException e) {
3516          LOG.warn("Failed to merge overlap group" + work, e.getCause());
3517        } catch (InterruptedException e) {
3518          LOG.error("Waiting for overlap merges was interrupted", e);
3519          return false;
3520        }
3521      }
3522      return true;
3523    }
3524
3525    /**
3526     * This dumps data in a visually reasonable way for visual debugging
3527     *
3528     * @param splits
3529     * @param regions
3530     */
3531    void dump(SortedSet<byte[]> splits, Multimap<byte[], HbckInfo> regions) {
3532      // we display this way because the last end key should be displayed as well.
3533      StringBuilder sb = new StringBuilder();
3534      for (byte[] k : splits) {
3535        sb.setLength(0); // clear out existing buffer, if any.
3536        sb.append(Bytes.toStringBinary(k) + ":\t");
3537        for (HbckInfo r : regions.get(k)) {
3538          sb.append("[ "+ r.toString() + ", "
3539              + Bytes.toStringBinary(r.getEndKey())+ "]\t");
3540        }
3541        errors.print(sb.toString());
3542      }
3543    }
3544  }
3545
3546  public void dumpOverlapProblems(Multimap<byte[], HbckInfo> regions) {
3547    // we display this way because the last end key should be displayed as
3548    // well.
3549    for (byte[] k : regions.keySet()) {
3550      errors.print(Bytes.toStringBinary(k) + ":");
3551      for (HbckInfo r : regions.get(k)) {
3552        errors.print("[ " + r.toString() + ", "
3553            + Bytes.toStringBinary(r.getEndKey()) + "]");
3554      }
3555      errors.print("----");
3556    }
3557  }
3558
3559  public void dumpSidelinedRegions(Map<Path, HbckInfo> regions) {
3560    for (Map.Entry<Path, HbckInfo> entry: regions.entrySet()) {
3561      TableName tableName = entry.getValue().getTableName();
3562      Path path = entry.getKey();
3563      errors.print("This sidelined region dir should be bulk loaded: "
3564        + path.toString());
3565      errors.print("Bulk load command looks like: "
3566        + "hbase org.apache.hadoop.hbase.tool.LoadIncrementalHFiles "
3567        + path.toUri().getPath() + " "+ tableName);
3568    }
3569  }
3570
3571  public Multimap<byte[], HbckInfo> getOverlapGroups(
3572      TableName table) {
3573    TableInfo ti = tablesInfo.get(table);
3574    return ti.overlapGroups;
3575  }
3576
3577  /**
3578   * Return a list of user-space table names whose metadata have not been
3579   * modified in the last few milliseconds specified by timelag
3580   * if any of the REGIONINFO_QUALIFIER, SERVER_QUALIFIER, STARTCODE_QUALIFIER,
3581   * SPLITA_QUALIFIER, SPLITB_QUALIFIER have not changed in the last
3582   * milliseconds specified by timelag, then the table is a candidate to be returned.
3583   * @return tables that have not been modified recently
3584   * @throws IOException if an error is encountered
3585   */
3586  TableDescriptor[] getTables(AtomicInteger numSkipped) {
3587    List<TableName> tableNames = new ArrayList<>();
3588    long now = EnvironmentEdgeManager.currentTime();
3589
3590    for (HbckInfo hbi : regionInfoMap.values()) {
3591      MetaEntry info = hbi.metaEntry;
3592
3593      // if the start key is zero, then we have found the first region of a table.
3594      // pick only those tables that were not modified in the last few milliseconds.
3595      if (info != null && info.getStartKey().length == 0 && !info.isMetaRegion()) {
3596        if (info.modTime + timelag < now) {
3597          tableNames.add(info.getTable());
3598        } else {
3599          numSkipped.incrementAndGet(); // one more in-flux table
3600        }
3601      }
3602    }
3603    return getTableDescriptors(tableNames);
3604  }
3605
3606  TableDescriptor[] getTableDescriptors(List<TableName> tableNames) {
3607      LOG.info("getTableDescriptors == tableNames => " + tableNames);
3608    try (Connection conn = ConnectionFactory.createConnection(getConf());
3609        Admin admin = conn.getAdmin()) {
3610      List<TableDescriptor> tds = admin.listTableDescriptors(tableNames);
3611      return tds.toArray(new TableDescriptor[tds.size()]);
3612    } catch (IOException e) {
3613      LOG.debug("Exception getting table descriptors", e);
3614    }
3615    return new TableDescriptor[0];
3616  }
3617
3618  /**
3619   * Gets the entry in regionInfo corresponding to the the given encoded
3620   * region name. If the region has not been seen yet, a new entry is added
3621   * and returned.
3622   */
3623  private synchronized HbckInfo getOrCreateInfo(String name) {
3624    HbckInfo hbi = regionInfoMap.get(name);
3625    if (hbi == null) {
3626      hbi = new HbckInfo(null);
3627      regionInfoMap.put(name, hbi);
3628    }
3629    return hbi;
3630  }
3631
3632  private void checkAndFixReplication() throws IOException {
3633    ReplicationChecker checker = new ReplicationChecker(getConf(), zkw, connection, errors);
3634    checker.checkUnDeletedQueues();
3635
3636    if (checker.hasUnDeletedQueues() && this.fixReplication) {
3637      checker.fixUnDeletedQueues();
3638      setShouldRerun();
3639    }
3640  }
3641
3642  /**
3643    * Check values in regionInfo for hbase:meta
3644    * Check if zero or more than one regions with hbase:meta are found.
3645    * If there are inconsistencies (i.e. zero or more than one regions
3646    * pretend to be holding the hbase:meta) try to fix that and report an error.
3647    * @throws IOException from HBaseFsckRepair functions
3648    * @throws KeeperException
3649    * @throws InterruptedException
3650    */
3651  boolean checkMetaRegion() throws IOException, KeeperException, InterruptedException {
3652    Map<Integer, HbckInfo> metaRegions = new HashMap<>();
3653    for (HbckInfo value : regionInfoMap.values()) {
3654      if (value.metaEntry != null && value.metaEntry.isMetaRegion()) {
3655        metaRegions.put(value.getReplicaId(), value);
3656      }
3657    }
3658    int metaReplication = admin.getTableDescriptor(TableName.META_TABLE_NAME)
3659        .getRegionReplication();
3660    boolean noProblem = true;
3661    // There will be always entries in regionInfoMap corresponding to hbase:meta & its replicas
3662    // Check the deployed servers. It should be exactly one server for each replica.
3663    for (int i = 0; i < metaReplication; i++) {
3664      HbckInfo metaHbckInfo = metaRegions.remove(i);
3665      List<ServerName> servers = new ArrayList<>();
3666      if (metaHbckInfo != null) {
3667        servers = metaHbckInfo.deployedOn;
3668      }
3669      if (servers.size() != 1) {
3670        noProblem = false;
3671        if (servers.isEmpty()) {
3672          assignMetaReplica(i);
3673        } else if (servers.size() > 1) {
3674          errors
3675          .reportError(ERROR_CODE.MULTI_META_REGION, "hbase:meta, replicaId " +
3676                       metaHbckInfo.getReplicaId() + " is found on more than one region.");
3677          if (shouldFixAssignments()) {
3678            errors.print("Trying to fix a problem with hbase:meta, replicaId " +
3679                         metaHbckInfo.getReplicaId() +"..");
3680            setShouldRerun();
3681            // try fix it (treat is a dupe assignment)
3682            HBaseFsckRepair.fixMultiAssignment(connection, metaHbckInfo.metaEntry, servers);
3683          }
3684        }
3685      }
3686    }
3687    // unassign whatever is remaining in metaRegions. They are excess replicas.
3688    for (Map.Entry<Integer, HbckInfo> entry : metaRegions.entrySet()) {
3689      noProblem = false;
3690      errors.reportError(ERROR_CODE.SHOULD_NOT_BE_DEPLOYED,
3691          "hbase:meta replicas are deployed in excess. Configured " + metaReplication +
3692          ", deployed " + metaRegions.size());
3693      if (shouldFixAssignments()) {
3694        errors.print("Trying to undeploy excess replica, replicaId: " + entry.getKey() +
3695            " of hbase:meta..");
3696        setShouldRerun();
3697        unassignMetaReplica(entry.getValue());
3698      }
3699    }
3700    // if noProblem is false, rerun hbck with hopefully fixed META
3701    // if noProblem is true, no errors, so continue normally
3702    return noProblem;
3703  }
3704
3705  private void unassignMetaReplica(HbckInfo hi) throws IOException, InterruptedException,
3706  KeeperException {
3707    undeployRegions(hi);
3708    ZKUtil.deleteNode(zkw, zkw.znodePaths.getZNodeForReplica(hi.metaEntry.getReplicaId()));
3709  }
3710
3711  private void assignMetaReplica(int replicaId)
3712      throws IOException, KeeperException, InterruptedException {
3713    errors.reportError(ERROR_CODE.NO_META_REGION, "hbase:meta, replicaId " +
3714        replicaId +" is not found on any region.");
3715    if (shouldFixAssignments()) {
3716      errors.print("Trying to fix a problem with hbase:meta..");
3717      setShouldRerun();
3718      // try to fix it (treat it as unassigned region)
3719      RegionInfo h = RegionReplicaUtil.getRegionInfoForReplica(
3720          RegionInfoBuilder.FIRST_META_REGIONINFO, replicaId);
3721      HBaseFsckRepair.fixUnassigned(admin, h);
3722      HBaseFsckRepair.waitUntilAssigned(admin, h);
3723    }
3724  }
3725
3726  /**
3727   * Scan hbase:meta, adding all regions found to the regionInfo map.
3728   * @throws IOException if an error is encountered
3729   */
3730  boolean loadMetaEntries() throws IOException {
3731    MetaTableAccessor.Visitor visitor = new MetaTableAccessor.Visitor() {
3732      int countRecord = 1;
3733
3734      // comparator to sort KeyValues with latest modtime
3735      final Comparator<Cell> comp = new Comparator<Cell>() {
3736        @Override
3737        public int compare(Cell k1, Cell k2) {
3738          return Long.compare(k1.getTimestamp(), k2.getTimestamp());
3739        }
3740      };
3741
3742      @Override
3743      public boolean visit(Result result) throws IOException {
3744        try {
3745
3746          // record the latest modification of this META record
3747          long ts =  Collections.max(result.listCells(), comp).getTimestamp();
3748          RegionLocations rl = MetaTableAccessor.getRegionLocations(result);
3749          if (rl == null) {
3750            emptyRegionInfoQualifiers.add(result);
3751            errors.reportError(ERROR_CODE.EMPTY_META_CELL,
3752              "Empty REGIONINFO_QUALIFIER found in hbase:meta");
3753            return true;
3754          }
3755          ServerName sn = null;
3756          if (rl.getRegionLocation(RegionInfo.DEFAULT_REPLICA_ID) == null ||
3757              rl.getRegionLocation(RegionInfo.DEFAULT_REPLICA_ID).getRegionInfo() == null) {
3758            emptyRegionInfoQualifiers.add(result);
3759            errors.reportError(ERROR_CODE.EMPTY_META_CELL,
3760              "Empty REGIONINFO_QUALIFIER found in hbase:meta");
3761            return true;
3762          }
3763          RegionInfo hri = rl.getRegionLocation(RegionInfo.DEFAULT_REPLICA_ID).getRegionInfo();
3764          if (!(isTableIncluded(hri.getTable())
3765              || hri.isMetaRegion())) {
3766            return true;
3767          }
3768          PairOfSameType<RegionInfo> daughters = MetaTableAccessor.getDaughterRegions(result);
3769          for (HRegionLocation h : rl.getRegionLocations()) {
3770            if (h == null || h.getRegionInfo() == null) {
3771              continue;
3772            }
3773            sn = h.getServerName();
3774            hri = h.getRegionInfo();
3775
3776            MetaEntry m = null;
3777            if (hri.getReplicaId() == RegionInfo.DEFAULT_REPLICA_ID) {
3778              m = new MetaEntry(hri, sn, ts, daughters.getFirst(), daughters.getSecond());
3779            } else {
3780              m = new MetaEntry(hri, sn, ts, null, null);
3781            }
3782            HbckInfo previous = regionInfoMap.get(hri.getEncodedName());
3783            if (previous == null) {
3784              regionInfoMap.put(hri.getEncodedName(), new HbckInfo(m));
3785            } else if (previous.metaEntry == null) {
3786              previous.metaEntry = m;
3787            } else {
3788              throw new IOException("Two entries in hbase:meta are same " + previous);
3789            }
3790          }
3791          PairOfSameType<RegionInfo> mergeRegions = MetaTableAccessor.getMergeRegions(result);
3792          for (RegionInfo mergeRegion : new RegionInfo[] {
3793              mergeRegions.getFirst(), mergeRegions.getSecond() }) {
3794            if (mergeRegion != null) {
3795              // This region is already been merged
3796              HbckInfo hbInfo = getOrCreateInfo(mergeRegion.getEncodedName());
3797              hbInfo.setMerged(true);
3798            }
3799          }
3800
3801          // show proof of progress to the user, once for every 100 records.
3802          if (countRecord % 100 == 0) {
3803            errors.progress();
3804          }
3805          countRecord++;
3806          return true;
3807        } catch (RuntimeException e) {
3808          LOG.error("Result=" + result);
3809          throw e;
3810        }
3811      }
3812    };
3813    if (!checkMetaOnly) {
3814      // Scan hbase:meta to pick up user regions
3815      MetaTableAccessor.fullScanRegions(connection, visitor);
3816    }
3817
3818    errors.print("");
3819    return true;
3820  }
3821
3822  /**
3823   * Stores the regioninfo entries scanned from META
3824   */
3825  static class MetaEntry extends HRegionInfo {
3826    ServerName regionServer;   // server hosting this region
3827    long modTime;          // timestamp of most recent modification metadata
3828    RegionInfo splitA, splitB; //split daughters
3829
3830    public MetaEntry(RegionInfo rinfo, ServerName regionServer, long modTime) {
3831      this(rinfo, regionServer, modTime, null, null);
3832    }
3833
3834    public MetaEntry(RegionInfo rinfo, ServerName regionServer, long modTime,
3835        RegionInfo splitA, RegionInfo splitB) {
3836      super(rinfo);
3837      this.regionServer = regionServer;
3838      this.modTime = modTime;
3839      this.splitA = splitA;
3840      this.splitB = splitB;
3841    }
3842
3843    @Override
3844    public boolean equals(Object o) {
3845      boolean superEq = super.equals(o);
3846      if (!superEq) {
3847        return superEq;
3848      }
3849
3850      MetaEntry me = (MetaEntry) o;
3851      if (!regionServer.equals(me.regionServer)) {
3852        return false;
3853      }
3854      return (modTime == me.modTime);
3855    }
3856
3857    @Override
3858    public int hashCode() {
3859      int hash = Arrays.hashCode(getRegionName());
3860      hash = (int) (hash ^ getRegionId());
3861      hash ^= Arrays.hashCode(getStartKey());
3862      hash ^= Arrays.hashCode(getEndKey());
3863      hash ^= Boolean.valueOf(isOffline()).hashCode();
3864      hash ^= getTable().hashCode();
3865      if (regionServer != null) {
3866        hash ^= regionServer.hashCode();
3867      }
3868      hash = (int) (hash ^ modTime);
3869      return hash;
3870    }
3871  }
3872
3873  /**
3874   * Stores the regioninfo entries from HDFS
3875   */
3876  static class HdfsEntry {
3877    RegionInfo hri;
3878    Path hdfsRegionDir = null;
3879    long hdfsRegionDirModTime  = 0;
3880    boolean hdfsRegioninfoFilePresent = false;
3881    boolean hdfsOnlyEdits = false;
3882  }
3883
3884  /**
3885   * Stores the regioninfo retrieved from Online region servers.
3886   */
3887  static class OnlineEntry {
3888    RegionInfo hri;
3889    ServerName hsa;
3890
3891    @Override
3892    public String toString() {
3893      return hsa.toString() + ";" + hri.getRegionNameAsString();
3894    }
3895  }
3896
3897  /**
3898   * Maintain information about a particular region.  It gathers information
3899   * from three places -- HDFS, META, and region servers.
3900   */
3901  public static class HbckInfo implements KeyRange {
3902    private MetaEntry metaEntry = null; // info in META
3903    private HdfsEntry hdfsEntry = null; // info in HDFS
3904    private List<OnlineEntry> deployedEntries = Lists.newArrayList(); // on Region Server
3905    private List<ServerName> deployedOn = Lists.newArrayList(); // info on RS's
3906    private boolean skipChecks = false; // whether to skip further checks to this region info.
3907    private boolean isMerged = false;// whether this region has already been merged into another one
3908    private int deployedReplicaId = RegionInfo.DEFAULT_REPLICA_ID;
3909    private RegionInfo primaryHRIForDeployedReplica = null;
3910
3911    HbckInfo(MetaEntry metaEntry) {
3912      this.metaEntry = metaEntry;
3913    }
3914
3915    public synchronized int getReplicaId() {
3916      return metaEntry != null? metaEntry.getReplicaId(): deployedReplicaId;
3917    }
3918
3919    public synchronized void addServer(RegionInfo hri, ServerName server) {
3920      OnlineEntry rse = new OnlineEntry() ;
3921      rse.hri = hri;
3922      rse.hsa = server;
3923      this.deployedEntries.add(rse);
3924      this.deployedOn.add(server);
3925      // save the replicaId that we see deployed in the cluster
3926      this.deployedReplicaId = hri.getReplicaId();
3927      this.primaryHRIForDeployedReplica =
3928          RegionReplicaUtil.getRegionInfoForDefaultReplica(hri);
3929    }
3930
3931    @Override
3932    public synchronized String toString() {
3933      StringBuilder sb = new StringBuilder();
3934      sb.append("{ meta => ");
3935      sb.append((metaEntry != null)? metaEntry.getRegionNameAsString() : "null");
3936      sb.append( ", hdfs => " + getHdfsRegionDir());
3937      sb.append( ", deployed => " + Joiner.on(", ").join(deployedEntries));
3938      sb.append( ", replicaId => " + getReplicaId());
3939      sb.append(" }");
3940      return sb.toString();
3941    }
3942
3943    @Override
3944    public byte[] getStartKey() {
3945      if (this.metaEntry != null) {
3946        return this.metaEntry.getStartKey();
3947      } else if (this.hdfsEntry != null) {
3948        return this.hdfsEntry.hri.getStartKey();
3949      } else {
3950        LOG.error("Entry " + this + " has no meta or hdfs region start key.");
3951        return null;
3952      }
3953    }
3954
3955    @Override
3956    public byte[] getEndKey() {
3957      if (this.metaEntry != null) {
3958        return this.metaEntry.getEndKey();
3959      } else if (this.hdfsEntry != null) {
3960        return this.hdfsEntry.hri.getEndKey();
3961      } else {
3962        LOG.error("Entry " + this + " has no meta or hdfs region start key.");
3963        return null;
3964      }
3965    }
3966
3967    public TableName getTableName() {
3968      if (this.metaEntry != null) {
3969        return this.metaEntry.getTable();
3970      } else if (this.hdfsEntry != null) {
3971        // we are only guaranteed to have a path and not an HRI for hdfsEntry,
3972        // so we get the name from the Path
3973        Path tableDir = this.hdfsEntry.hdfsRegionDir.getParent();
3974        return FSUtils.getTableName(tableDir);
3975      } else {
3976        // return the info from the first online/deployed hri
3977        for (OnlineEntry e : deployedEntries) {
3978          return e.hri.getTable();
3979        }
3980        return null;
3981      }
3982    }
3983
3984    public String getRegionNameAsString() {
3985      if (metaEntry != null) {
3986        return metaEntry.getRegionNameAsString();
3987      } else if (hdfsEntry != null) {
3988        if (hdfsEntry.hri != null) {
3989          return hdfsEntry.hri.getRegionNameAsString();
3990        }
3991      } else {
3992        // return the info from the first online/deployed hri
3993        for (OnlineEntry e : deployedEntries) {
3994          return e.hri.getRegionNameAsString();
3995        }
3996      }
3997      return null;
3998    }
3999
4000    public byte[] getRegionName() {
4001      if (metaEntry != null) {
4002        return metaEntry.getRegionName();
4003      } else if (hdfsEntry != null) {
4004        return hdfsEntry.hri.getRegionName();
4005      } else {
4006        // return the info from the first online/deployed hri
4007        for (OnlineEntry e : deployedEntries) {
4008          return e.hri.getRegionName();
4009        }
4010        return null;
4011      }
4012    }
4013
4014    public RegionInfo getPrimaryHRIForDeployedReplica() {
4015      return primaryHRIForDeployedReplica;
4016    }
4017
4018    Path getHdfsRegionDir() {
4019      if (hdfsEntry == null) {
4020        return null;
4021      }
4022      return hdfsEntry.hdfsRegionDir;
4023    }
4024
4025    boolean containsOnlyHdfsEdits() {
4026      if (hdfsEntry == null) {
4027        return false;
4028      }
4029      return hdfsEntry.hdfsOnlyEdits;
4030    }
4031
4032    boolean isHdfsRegioninfoPresent() {
4033      if (hdfsEntry == null) {
4034        return false;
4035      }
4036      return hdfsEntry.hdfsRegioninfoFilePresent;
4037    }
4038
4039    long getModTime() {
4040      if (hdfsEntry == null) {
4041        return 0;
4042      }
4043      return hdfsEntry.hdfsRegionDirModTime;
4044    }
4045
4046    RegionInfo getHdfsHRI() {
4047      if (hdfsEntry == null) {
4048        return null;
4049      }
4050      return hdfsEntry.hri;
4051    }
4052
4053    public void setSkipChecks(boolean skipChecks) {
4054      this.skipChecks = skipChecks;
4055    }
4056
4057    public boolean isSkipChecks() {
4058      return skipChecks;
4059    }
4060
4061    public void setMerged(boolean isMerged) {
4062      this.isMerged = isMerged;
4063    }
4064
4065    public boolean isMerged() {
4066      return this.isMerged;
4067    }
4068  }
4069
4070  final static Comparator<HbckInfo> cmp = new Comparator<HbckInfo>() {
4071    @Override
4072    public int compare(HbckInfo l, HbckInfo r) {
4073      if (l == r) {
4074        // same instance
4075        return 0;
4076      }
4077
4078      int tableCompare = l.getTableName().compareTo(r.getTableName());
4079      if (tableCompare != 0) {
4080        return tableCompare;
4081      }
4082
4083      int startComparison = RegionSplitCalculator.BYTES_COMPARATOR.compare(
4084          l.getStartKey(), r.getStartKey());
4085      if (startComparison != 0) {
4086        return startComparison;
4087      }
4088
4089      // Special case for absolute endkey
4090      byte[] endKey = r.getEndKey();
4091      endKey = (endKey.length == 0) ? null : endKey;
4092      byte[] endKey2 = l.getEndKey();
4093      endKey2 = (endKey2.length == 0) ? null : endKey2;
4094      int endComparison = RegionSplitCalculator.BYTES_COMPARATOR.compare(
4095          endKey2,  endKey);
4096
4097      if (endComparison != 0) {
4098        return endComparison;
4099      }
4100
4101      // use regionId as tiebreaker.
4102      // Null is considered after all possible values so make it bigger.
4103      if (l.hdfsEntry == null && r.hdfsEntry == null) {
4104        return 0;
4105      }
4106      if (l.hdfsEntry == null && r.hdfsEntry != null) {
4107        return 1;
4108      }
4109      // l.hdfsEntry must not be null
4110      if (r.hdfsEntry == null) {
4111        return -1;
4112      }
4113      // both l.hdfsEntry and r.hdfsEntry must not be null.
4114      return Long.compare(l.hdfsEntry.hri.getRegionId(), r.hdfsEntry.hri.getRegionId());
4115    }
4116  };
4117
4118  /**
4119   * Prints summary of all tables found on the system.
4120   */
4121  private void printTableSummary(SortedMap<TableName, TableInfo> tablesInfo) {
4122    StringBuilder sb = new StringBuilder();
4123    int numOfSkippedRegions;
4124    errors.print("Summary:");
4125    for (TableInfo tInfo : tablesInfo.values()) {
4126      numOfSkippedRegions = (skippedRegions.containsKey(tInfo.getName())) ?
4127          skippedRegions.get(tInfo.getName()).size() : 0;
4128
4129      if (errors.tableHasErrors(tInfo)) {
4130        errors.print("Table " + tInfo.getName() + " is inconsistent.");
4131      } else if (numOfSkippedRegions > 0){
4132        errors.print("Table " + tInfo.getName() + " is okay (with "
4133          + numOfSkippedRegions + " skipped regions).");
4134      }
4135      else {
4136        errors.print("Table " + tInfo.getName() + " is okay.");
4137      }
4138      errors.print("    Number of regions: " + tInfo.getNumRegions());
4139      if (numOfSkippedRegions > 0) {
4140        Set<String> skippedRegionStrings = skippedRegions.get(tInfo.getName());
4141        System.out.println("    Number of skipped regions: " + numOfSkippedRegions);
4142        System.out.println("      List of skipped regions:");
4143        for(String sr : skippedRegionStrings) {
4144          System.out.println("        " + sr);
4145        }
4146      }
4147      sb.setLength(0); // clear out existing buffer, if any.
4148      sb.append("    Deployed on: ");
4149      for (ServerName server : tInfo.deployedOn) {
4150        sb.append(" " + server.toString());
4151      }
4152      errors.print(sb.toString());
4153    }
4154  }
4155
4156  static ErrorReporter getErrorReporter(
4157      final Configuration conf) throws ClassNotFoundException {
4158    Class<? extends ErrorReporter> reporter = conf.getClass("hbasefsck.errorreporter", PrintingErrorReporter.class, ErrorReporter.class);
4159    return ReflectionUtils.newInstance(reporter, conf);
4160  }
4161
4162  public interface ErrorReporter {
4163    enum ERROR_CODE {
4164      UNKNOWN, NO_META_REGION, NULL_META_REGION, NO_VERSION_FILE, NOT_IN_META_HDFS, NOT_IN_META,
4165      NOT_IN_META_OR_DEPLOYED, NOT_IN_HDFS_OR_DEPLOYED, NOT_IN_HDFS, SERVER_DOES_NOT_MATCH_META,
4166      NOT_DEPLOYED,
4167      MULTI_DEPLOYED, SHOULD_NOT_BE_DEPLOYED, MULTI_META_REGION, RS_CONNECT_FAILURE,
4168      FIRST_REGION_STARTKEY_NOT_EMPTY, LAST_REGION_ENDKEY_NOT_EMPTY, DUPE_STARTKEYS,
4169      HOLE_IN_REGION_CHAIN, OVERLAP_IN_REGION_CHAIN, REGION_CYCLE, DEGENERATE_REGION,
4170      ORPHAN_HDFS_REGION, LINGERING_SPLIT_PARENT, NO_TABLEINFO_FILE, LINGERING_REFERENCE_HFILE,
4171      LINGERING_HFILELINK, WRONG_USAGE, EMPTY_META_CELL, EXPIRED_TABLE_LOCK, BOUNDARIES_ERROR,
4172      ORPHAN_TABLE_STATE, NO_TABLE_STATE, UNDELETED_REPLICATION_QUEUE, DUPE_ENDKEYS,
4173      UNSUPPORTED_OPTION
4174    }
4175    void clear();
4176    void report(String message);
4177    void reportError(String message);
4178    void reportError(ERROR_CODE errorCode, String message);
4179    void reportError(ERROR_CODE errorCode, String message, TableInfo table);
4180    void reportError(ERROR_CODE errorCode, String message, TableInfo table, HbckInfo info);
4181    void reportError(
4182      ERROR_CODE errorCode,
4183      String message,
4184      TableInfo table,
4185      HbckInfo info1,
4186      HbckInfo info2
4187    );
4188    int summarize();
4189    void detail(String details);
4190    ArrayList<ERROR_CODE> getErrorList();
4191    void progress();
4192    void print(String message);
4193    void resetErrors();
4194    boolean tableHasErrors(TableInfo table);
4195  }
4196
4197  static class PrintingErrorReporter implements ErrorReporter {
4198    public int errorCount = 0;
4199    private int showProgress;
4200    // How frequently calls to progress() will create output
4201    private static final int progressThreshold = 100;
4202
4203    Set<TableInfo> errorTables = new HashSet<>();
4204
4205    // for use by unit tests to verify which errors were discovered
4206    private ArrayList<ERROR_CODE> errorList = new ArrayList<>();
4207
4208    @Override
4209    public void clear() {
4210      errorTables.clear();
4211      errorList.clear();
4212      errorCount = 0;
4213    }
4214
4215    @Override
4216    public synchronized void reportError(ERROR_CODE errorCode, String message) {
4217      if (errorCode == ERROR_CODE.WRONG_USAGE) {
4218        System.err.println(message);
4219        return;
4220      }
4221
4222      errorList.add(errorCode);
4223      if (!summary) {
4224        System.out.println("ERROR: " + message);
4225      }
4226      errorCount++;
4227      showProgress = 0;
4228    }
4229
4230    @Override
4231    public synchronized void reportError(ERROR_CODE errorCode, String message, TableInfo table) {
4232      errorTables.add(table);
4233      reportError(errorCode, message);
4234    }
4235
4236    @Override
4237    public synchronized void reportError(ERROR_CODE errorCode, String message, TableInfo table,
4238                                         HbckInfo info) {
4239      errorTables.add(table);
4240      String reference = "(region " + info.getRegionNameAsString() + ")";
4241      reportError(errorCode, reference + " " + message);
4242    }
4243
4244    @Override
4245    public synchronized void reportError(ERROR_CODE errorCode, String message, TableInfo table,
4246                                         HbckInfo info1, HbckInfo info2) {
4247      errorTables.add(table);
4248      String reference = "(regions " + info1.getRegionNameAsString()
4249          + " and " + info2.getRegionNameAsString() + ")";
4250      reportError(errorCode, reference + " " + message);
4251    }
4252
4253    @Override
4254    public synchronized void reportError(String message) {
4255      reportError(ERROR_CODE.UNKNOWN, message);
4256    }
4257
4258    /**
4259     * Report error information, but do not increment the error count.  Intended for cases
4260     * where the actual error would have been reported previously.
4261     * @param message
4262     */
4263    @Override
4264    public synchronized void report(String message) {
4265      if (! summary) {
4266        System.out.println("ERROR: " + message);
4267      }
4268      showProgress = 0;
4269    }
4270
4271    @Override
4272    public synchronized int summarize() {
4273      System.out.println(Integer.toString(errorCount) +
4274                         " inconsistencies detected.");
4275      if (errorCount == 0) {
4276        System.out.println("Status: OK");
4277        return 0;
4278      } else {
4279        System.out.println("Status: INCONSISTENT");
4280        return -1;
4281      }
4282    }
4283
4284    @Override
4285    public ArrayList<ERROR_CODE> getErrorList() {
4286      return errorList;
4287    }
4288
4289    @Override
4290    public synchronized void print(String message) {
4291      if (!summary) {
4292        System.out.println(message);
4293      }
4294    }
4295
4296    @Override
4297    public boolean tableHasErrors(TableInfo table) {
4298      return errorTables.contains(table);
4299    }
4300
4301    @Override
4302    public void resetErrors() {
4303      errorCount = 0;
4304    }
4305
4306    @Override
4307    public synchronized void detail(String message) {
4308      if (details) {
4309        System.out.println(message);
4310      }
4311      showProgress = 0;
4312    }
4313
4314    @Override
4315    public synchronized void progress() {
4316      if (showProgress++ == progressThreshold) {
4317        if (!summary) {
4318          System.out.print(".");
4319        }
4320        showProgress = 0;
4321      }
4322    }
4323  }
4324
4325  /**
4326   * Contact a region server and get all information from it
4327   */
4328  static class WorkItemRegion implements Callable<Void> {
4329    private final HBaseFsck hbck;
4330    private final ServerName rsinfo;
4331    private final ErrorReporter errors;
4332    private final ClusterConnection connection;
4333
4334    WorkItemRegion(HBaseFsck hbck, ServerName info,
4335                   ErrorReporter errors, ClusterConnection connection) {
4336      this.hbck = hbck;
4337      this.rsinfo = info;
4338      this.errors = errors;
4339      this.connection = connection;
4340    }
4341
4342    @Override
4343    public synchronized Void call() throws IOException {
4344      errors.progress();
4345      try {
4346        BlockingInterface server = connection.getAdmin(rsinfo);
4347
4348        // list all online regions from this region server
4349        List<RegionInfo> regions = ProtobufUtil.getOnlineRegions(server);
4350        regions = filterRegions(regions);
4351
4352        if (details) {
4353          errors.detail("RegionServer: " + rsinfo.getServerName() +
4354                           " number of regions: " + regions.size());
4355          for (RegionInfo rinfo: regions) {
4356            errors.detail("  " + rinfo.getRegionNameAsString() +
4357                             " id: " + rinfo.getRegionId() +
4358                             " encoded_name: " + rinfo.getEncodedName() +
4359                             " start: " + Bytes.toStringBinary(rinfo.getStartKey()) +
4360                             " end: " + Bytes.toStringBinary(rinfo.getEndKey()));
4361          }
4362        }
4363
4364        // check to see if the existence of this region matches the region in META
4365        for (RegionInfo r:regions) {
4366          HbckInfo hbi = hbck.getOrCreateInfo(r.getEncodedName());
4367          hbi.addServer(r, rsinfo);
4368        }
4369      } catch (IOException e) {          // unable to connect to the region server.
4370        errors.reportError(ERROR_CODE.RS_CONNECT_FAILURE, "RegionServer: " + rsinfo.getServerName() +
4371          " Unable to fetch region information. " + e);
4372        throw e;
4373      }
4374      return null;
4375    }
4376
4377    private List<RegionInfo> filterRegions(List<RegionInfo> regions) {
4378      List<RegionInfo> ret = Lists.newArrayList();
4379      for (RegionInfo hri : regions) {
4380        if (hri.isMetaRegion() || (!hbck.checkMetaOnly
4381            && hbck.isTableIncluded(hri.getTable()))) {
4382          ret.add(hri);
4383        }
4384      }
4385      return ret;
4386    }
4387  }
4388
4389  /**
4390   * Contact hdfs and get all information about specified table directory into
4391   * regioninfo list.
4392   */
4393  class WorkItemHdfsDir implements Callable<Void> {
4394    private FileStatus tableDir;
4395    private ErrorReporter errors;
4396    private FileSystem fs;
4397
4398    WorkItemHdfsDir(FileSystem fs, ErrorReporter errors,
4399                    FileStatus status) {
4400      this.fs = fs;
4401      this.tableDir = status;
4402      this.errors = errors;
4403    }
4404
4405    @Override
4406    public synchronized Void call() throws InterruptedException, ExecutionException {
4407      final Vector<Exception> exceptions = new Vector<>();
4408
4409      try {
4410        final FileStatus[] regionDirs = fs.listStatus(tableDir.getPath());
4411        final List<Future<?>> futures = new ArrayList<>(regionDirs.length);
4412
4413        for (final FileStatus regionDir : regionDirs) {
4414          errors.progress();
4415          final String encodedName = regionDir.getPath().getName();
4416          // ignore directories that aren't hexadecimal
4417          if (!encodedName.toLowerCase(Locale.ROOT).matches("[0-9a-f]+")) {
4418            continue;
4419          }
4420
4421          if (!exceptions.isEmpty()) {
4422            break;
4423          }
4424
4425          futures.add(executor.submit(new Runnable() {
4426            @Override
4427            public void run() {
4428              try {
4429                LOG.debug("Loading region info from hdfs:"+ regionDir.getPath());
4430
4431                Path regioninfoFile = new Path(regionDir.getPath(), HRegionFileSystem.REGION_INFO_FILE);
4432                boolean regioninfoFileExists = fs.exists(regioninfoFile);
4433
4434                if (!regioninfoFileExists) {
4435                  // As tables become larger it is more and more likely that by the time you
4436                  // reach a given region that it will be gone due to region splits/merges.
4437                  if (!fs.exists(regionDir.getPath())) {
4438                    LOG.warn("By the time we tried to process this region dir it was already gone: "
4439                        + regionDir.getPath());
4440                    return;
4441                  }
4442                }
4443
4444                HbckInfo hbi = HBaseFsck.this.getOrCreateInfo(encodedName);
4445                HdfsEntry he = new HdfsEntry();
4446                synchronized (hbi) {
4447                  if (hbi.getHdfsRegionDir() != null) {
4448                    errors.print("Directory " + encodedName + " duplicate??" +
4449                                 hbi.getHdfsRegionDir());
4450                  }
4451
4452                  he.hdfsRegionDir = regionDir.getPath();
4453                  he.hdfsRegionDirModTime = regionDir.getModificationTime();
4454                  he.hdfsRegioninfoFilePresent = regioninfoFileExists;
4455                  // we add to orphan list when we attempt to read .regioninfo
4456
4457                  // Set a flag if this region contains only edits
4458                  // This is special case if a region is left after split
4459                  he.hdfsOnlyEdits = true;
4460                  FileStatus[] subDirs = fs.listStatus(regionDir.getPath());
4461                  Path ePath = WALSplitter.getRegionDirRecoveredEditsDir(regionDir.getPath());
4462                  for (FileStatus subDir : subDirs) {
4463                    errors.progress();
4464                    String sdName = subDir.getPath().getName();
4465                    if (!sdName.startsWith(".") && !sdName.equals(ePath.getName())) {
4466                      he.hdfsOnlyEdits = false;
4467                      break;
4468                    }
4469                  }
4470                  hbi.hdfsEntry = he;
4471                }
4472              } catch (Exception e) {
4473                LOG.error("Could not load region dir", e);
4474                exceptions.add(e);
4475              }
4476            }
4477          }));
4478        }
4479
4480        // Ensure all pending tasks are complete (or that we run into an exception)
4481        for (Future<?> f : futures) {
4482          if (!exceptions.isEmpty()) {
4483            break;
4484          }
4485          try {
4486            f.get();
4487          } catch (ExecutionException e) {
4488            LOG.error("Unexpected exec exception!  Should've been caught already.  (Bug?)", e);
4489            // Shouldn't happen, we already logged/caught any exceptions in the Runnable
4490          };
4491        }
4492      } catch (IOException e) {
4493        LOG.error("Cannot execute WorkItemHdfsDir for " + tableDir, e);
4494        exceptions.add(e);
4495      } finally {
4496        if (!exceptions.isEmpty()) {
4497          errors.reportError(ERROR_CODE.RS_CONNECT_FAILURE, "Table Directory: "
4498              + tableDir.getPath().getName()
4499              + " Unable to fetch all HDFS region information. ");
4500          // Just throw the first exception as an indication something bad happened
4501          // Don't need to propagate all the exceptions, we already logged them all anyway
4502          throw new ExecutionException("First exception in WorkItemHdfsDir", exceptions.firstElement());
4503        }
4504      }
4505      return null;
4506    }
4507  }
4508
4509  /**
4510   * Contact hdfs and get all information about specified table directory into
4511   * regioninfo list.
4512   */
4513  static class WorkItemHdfsRegionInfo implements Callable<Void> {
4514    private HbckInfo hbi;
4515    private HBaseFsck hbck;
4516    private ErrorReporter errors;
4517
4518    WorkItemHdfsRegionInfo(HbckInfo hbi, HBaseFsck hbck, ErrorReporter errors) {
4519      this.hbi = hbi;
4520      this.hbck = hbck;
4521      this.errors = errors;
4522    }
4523
4524    @Override
4525    public synchronized Void call() throws IOException {
4526      // only load entries that haven't been loaded yet.
4527      if (hbi.getHdfsHRI() == null) {
4528        try {
4529          errors.progress();
4530          hbck.loadHdfsRegioninfo(hbi);
4531        } catch (IOException ioe) {
4532          String msg = "Orphan region in HDFS: Unable to load .regioninfo from table "
4533              + hbi.getTableName() + " in hdfs dir "
4534              + hbi.getHdfsRegionDir()
4535              + "!  It may be an invalid format or version file.  Treating as "
4536              + "an orphaned regiondir.";
4537          errors.reportError(ERROR_CODE.ORPHAN_HDFS_REGION, msg);
4538          try {
4539            hbck.debugLsr(hbi.getHdfsRegionDir());
4540          } catch (IOException ioe2) {
4541            LOG.error("Unable to read directory " + hbi.getHdfsRegionDir(), ioe2);
4542            throw ioe2;
4543          }
4544          hbck.orphanHdfsDirs.add(hbi);
4545          throw ioe;
4546        }
4547      }
4548      return null;
4549    }
4550  };
4551
4552  /**
4553   * Display the full report from fsck. This displays all live and dead region
4554   * servers, and all known regions.
4555   */
4556  public static void setDisplayFullReport() {
4557    details = true;
4558  }
4559
4560  /**
4561   * Set exclusive mode.
4562   */
4563  public static void setForceExclusive() {
4564    forceExclusive = true;
4565  }
4566
4567  /**
4568   * Only one instance of hbck can modify HBase at a time.
4569   */
4570  public boolean isExclusive() {
4571    return fixAny || forceExclusive;
4572  }
4573
4574  /**
4575   * Set summary mode.
4576   * Print only summary of the tables and status (OK or INCONSISTENT)
4577   */
4578  static void setSummary() {
4579    summary = true;
4580  }
4581
4582  /**
4583   * Set hbase:meta check mode.
4584   * Print only info about hbase:meta table deployment/state
4585   */
4586  void setCheckMetaOnly() {
4587    checkMetaOnly = true;
4588  }
4589
4590  /**
4591   * Set region boundaries check mode.
4592   */
4593  void setRegionBoundariesCheck() {
4594    checkRegionBoundaries = true;
4595  }
4596
4597  /**
4598   * Set replication fix mode.
4599   */
4600  public void setFixReplication(boolean shouldFix) {
4601    fixReplication = shouldFix;
4602    fixAny |= shouldFix;
4603  }
4604
4605  /**
4606   * Check if we should rerun fsck again. This checks if we've tried to
4607   * fix something and we should rerun fsck tool again.
4608   * Display the full report from fsck. This displays all live and dead
4609   * region servers, and all known regions.
4610   */
4611  void setShouldRerun() {
4612    rerun = true;
4613  }
4614
4615  boolean shouldRerun() {
4616    return rerun;
4617  }
4618
4619  /**
4620   * Fix inconsistencies found by fsck. This should try to fix errors (if any)
4621   * found by fsck utility.
4622   */
4623  public void setFixAssignments(boolean shouldFix) {
4624    fixAssignments = shouldFix;
4625    fixAny |= shouldFix;
4626  }
4627
4628  boolean shouldFixAssignments() {
4629    return fixAssignments;
4630  }
4631
4632  public void setFixMeta(boolean shouldFix) {
4633    fixMeta = shouldFix;
4634    fixAny |= shouldFix;
4635  }
4636
4637  boolean shouldFixMeta() {
4638    return fixMeta;
4639  }
4640
4641  public void setFixEmptyMetaCells(boolean shouldFix) {
4642    fixEmptyMetaCells = shouldFix;
4643    fixAny |= shouldFix;
4644  }
4645
4646  boolean shouldFixEmptyMetaCells() {
4647    return fixEmptyMetaCells;
4648  }
4649
4650  public void setCheckHdfs(boolean checking) {
4651    checkHdfs = checking;
4652  }
4653
4654  boolean shouldCheckHdfs() {
4655    return checkHdfs;
4656  }
4657
4658  public void setFixHdfsHoles(boolean shouldFix) {
4659    fixHdfsHoles = shouldFix;
4660    fixAny |= shouldFix;
4661  }
4662
4663  boolean shouldFixHdfsHoles() {
4664    return fixHdfsHoles;
4665  }
4666
4667  public void setFixTableOrphans(boolean shouldFix) {
4668    fixTableOrphans = shouldFix;
4669    fixAny |= shouldFix;
4670  }
4671
4672  boolean shouldFixTableOrphans() {
4673    return fixTableOrphans;
4674  }
4675
4676  public void setFixHdfsOverlaps(boolean shouldFix) {
4677    fixHdfsOverlaps = shouldFix;
4678    fixAny |= shouldFix;
4679  }
4680
4681  boolean shouldFixHdfsOverlaps() {
4682    return fixHdfsOverlaps;
4683  }
4684
4685  public void setFixHdfsOrphans(boolean shouldFix) {
4686    fixHdfsOrphans = shouldFix;
4687    fixAny |= shouldFix;
4688  }
4689
4690  boolean shouldFixHdfsOrphans() {
4691    return fixHdfsOrphans;
4692  }
4693
4694  public void setFixVersionFile(boolean shouldFix) {
4695    fixVersionFile = shouldFix;
4696    fixAny |= shouldFix;
4697  }
4698
4699  public boolean shouldFixVersionFile() {
4700    return fixVersionFile;
4701  }
4702
4703  public void setSidelineBigOverlaps(boolean sbo) {
4704    this.sidelineBigOverlaps = sbo;
4705  }
4706
4707  public boolean shouldSidelineBigOverlaps() {
4708    return sidelineBigOverlaps;
4709  }
4710
4711  public void setFixSplitParents(boolean shouldFix) {
4712    fixSplitParents = shouldFix;
4713    fixAny |= shouldFix;
4714  }
4715
4716  public void setRemoveParents(boolean shouldFix) {
4717    removeParents = shouldFix;
4718    fixAny |= shouldFix;
4719  }
4720
4721  boolean shouldFixSplitParents() {
4722    return fixSplitParents;
4723  }
4724
4725  boolean shouldRemoveParents() {
4726    return removeParents;
4727  }
4728
4729  public void setFixReferenceFiles(boolean shouldFix) {
4730    fixReferenceFiles = shouldFix;
4731    fixAny |= shouldFix;
4732  }
4733
4734  boolean shouldFixReferenceFiles() {
4735    return fixReferenceFiles;
4736  }
4737
4738  public void setFixHFileLinks(boolean shouldFix) {
4739    fixHFileLinks = shouldFix;
4740    fixAny |= shouldFix;
4741  }
4742
4743  boolean shouldFixHFileLinks() {
4744    return fixHFileLinks;
4745  }
4746
4747  public boolean shouldIgnorePreCheckPermission() {
4748    return !fixAny || ignorePreCheckPermission;
4749  }
4750
4751  public void setIgnorePreCheckPermission(boolean ignorePreCheckPermission) {
4752    this.ignorePreCheckPermission = ignorePreCheckPermission;
4753  }
4754
4755  /**
4756   * @param mm maximum number of regions to merge into a single region.
4757   */
4758  public void setMaxMerge(int mm) {
4759    this.maxMerge = mm;
4760  }
4761
4762  public int getMaxMerge() {
4763    return maxMerge;
4764  }
4765
4766  public void setMaxOverlapsToSideline(int mo) {
4767    this.maxOverlapsToSideline = mo;
4768  }
4769
4770  public int getMaxOverlapsToSideline() {
4771    return maxOverlapsToSideline;
4772  }
4773
4774  /**
4775   * Only check/fix tables specified by the list,
4776   * Empty list means all tables are included.
4777   */
4778  boolean isTableIncluded(TableName table) {
4779    return (tablesIncluded.isEmpty()) || tablesIncluded.contains(table);
4780  }
4781
4782  public void includeTable(TableName table) {
4783    tablesIncluded.add(table);
4784  }
4785
4786  Set<TableName> getIncludedTables() {
4787    return new HashSet<>(tablesIncluded);
4788  }
4789
4790  /**
4791   * We are interested in only those tables that have not changed their state in
4792   * hbase:meta during the last few seconds specified by hbase.admin.fsck.timelag
4793   * @param seconds - the time in seconds
4794   */
4795  public void setTimeLag(long seconds) {
4796    timelag = seconds * 1000; // convert to milliseconds
4797  }
4798
4799  /**
4800   *
4801   * @param sidelineDir - HDFS path to sideline data
4802   */
4803  public void setSidelineDir(String sidelineDir) {
4804    this.sidelineDir = new Path(sidelineDir);
4805  }
4806
4807  protected HFileCorruptionChecker createHFileCorruptionChecker(boolean sidelineCorruptHFiles) throws IOException {
4808    return new HFileCorruptionChecker(getConf(), executor, sidelineCorruptHFiles);
4809  }
4810
4811  public HFileCorruptionChecker getHFilecorruptionChecker() {
4812    return hfcc;
4813  }
4814
4815  public void setHFileCorruptionChecker(HFileCorruptionChecker hfcc) {
4816    this.hfcc = hfcc;
4817  }
4818
4819  public void setRetCode(int code) {
4820    this.retcode = code;
4821  }
4822
4823  public int getRetCode() {
4824    return retcode;
4825  }
4826
4827  protected HBaseFsck printUsageAndExit() {
4828    StringWriter sw = new StringWriter(2048);
4829    PrintWriter out = new PrintWriter(sw);
4830    out.println("");
4831    out.println("-----------------------------------------------------------------------");
4832    out.println("NOTE: As of HBase version 2.0, the hbck tool is significantly changed.");
4833    out.println("In general, all Read-Only options are supported and can be be used");
4834    out.println("safely. Most -fix/ -repair options are NOT supported. Please see usage");
4835    out.println("below for details on which options are not supported.");
4836    out.println("-----------------------------------------------------------------------");
4837    out.println("");
4838    out.println("Usage: fsck [opts] {only tables}");
4839    out.println(" where [opts] are:");
4840    out.println("   -help Display help options (this)");
4841    out.println("   -details Display full report of all regions.");
4842    out.println("   -timelag <timeInSeconds>  Process only regions that " +
4843                       " have not experienced any metadata updates in the last " +
4844                       " <timeInSeconds> seconds.");
4845    out.println("   -sleepBeforeRerun <timeInSeconds> Sleep this many seconds" +
4846        " before checking if the fix worked if run with -fix");
4847    out.println("   -summary Print only summary of the tables and status.");
4848    out.println("   -metaonly Only check the state of the hbase:meta table.");
4849    out.println("   -sidelineDir <hdfs://> HDFS path to backup existing meta.");
4850    out.println("   -boundaries Verify that regions boundaries are the same between META and store files.");
4851    out.println("   -exclusive Abort if another hbck is exclusive or fixing.");
4852
4853    out.println("");
4854    out.println("  Datafile Repair options: (expert features, use with caution!)");
4855    out.println("   -checkCorruptHFiles     Check all Hfiles by opening them to make sure they are valid");
4856    out.println("   -sidelineCorruptHFiles  Quarantine corrupted HFiles.  implies -checkCorruptHFiles");
4857
4858    out.println("");
4859    out.println(" Replication options");
4860    out.println("   -fixReplication   Deletes replication queues for removed peers");
4861
4862    out.println("");
4863    out.println("  Metadata Repair options supported as of version 2.0: (expert features, use with caution!)");
4864    out.println("   -fixVersionFile   Try to fix missing hbase.version file in hdfs.");
4865    out.println("   -fixReferenceFiles  Try to offline lingering reference store files");
4866    out.println("   -fixHFileLinks  Try to offline lingering HFileLinks");
4867    out.println("   -noHdfsChecking   Don't load/check region info from HDFS."
4868        + " Assumes hbase:meta region info is good. Won't check/fix any HDFS issue, e.g. hole, orphan, or overlap");
4869    out.println("   -ignorePreCheckPermission  ignore filesystem permission pre-check");
4870
4871    out.println("");
4872    out.println("NOTE: Following options are NOT supported as of HBase version 2.0+.");
4873    out.println("");
4874    out.println("  UNSUPPORTED Metadata Repair options: (expert features, use with caution!)");
4875    out.println("   -fix              Try to fix region assignments.  This is for backwards compatiblity");
4876    out.println("   -fixAssignments   Try to fix region assignments.  Replaces the old -fix");
4877    out.println("   -fixMeta          Try to fix meta problems.  This assumes HDFS region info is good.");
4878    out.println("   -fixHdfsHoles     Try to fix region holes in hdfs.");
4879    out.println("   -fixHdfsOrphans   Try to fix region dirs with no .regioninfo file in hdfs");
4880    out.println("   -fixTableOrphans  Try to fix table dirs with no .tableinfo file in hdfs (online mode only)");
4881    out.println("   -fixHdfsOverlaps  Try to fix region overlaps in hdfs.");
4882    out.println("   -maxMerge <n>     When fixing region overlaps, allow at most <n> regions to merge. (n=" + DEFAULT_MAX_MERGE +" by default)");
4883    out.println("   -sidelineBigOverlaps  When fixing region overlaps, allow to sideline big overlaps");
4884    out.println("   -maxOverlapsToSideline <n>  When fixing region overlaps, allow at most <n> regions to sideline per group. (n=" + DEFAULT_OVERLAPS_TO_SIDELINE +" by default)");
4885    out.println("   -fixSplitParents  Try to force offline split parents to be online.");
4886    out.println("   -removeParents    Try to offline and sideline lingering parents and keep daughter regions.");
4887    out.println("   -fixEmptyMetaCells  Try to fix hbase:meta entries not referencing any region"
4888        + " (empty REGIONINFO_QUALIFIER rows)");
4889
4890    out.println("");
4891    out.println("  UNSUPPORTED Metadata Repair shortcuts");
4892    out.println("   -repair           Shortcut for -fixAssignments -fixMeta -fixHdfsHoles " +
4893        "-fixHdfsOrphans -fixHdfsOverlaps -fixVersionFile -sidelineBigOverlaps -fixReferenceFiles" +
4894        "-fixHFileLinks");
4895    out.println("   -repairHoles      Shortcut for -fixAssignments -fixMeta -fixHdfsHoles");
4896
4897    out.flush();
4898    errors.reportError(ERROR_CODE.WRONG_USAGE, sw.toString());
4899
4900    setRetCode(-2);
4901    return this;
4902  }
4903
4904  /**
4905   * Main program
4906   *
4907   * @param args
4908   * @throws Exception
4909   */
4910  public static void main(String[] args) throws Exception {
4911    // create a fsck object
4912    Configuration conf = HBaseConfiguration.create();
4913    Path hbasedir = FSUtils.getRootDir(conf);
4914    URI defaultFs = hbasedir.getFileSystem(conf).getUri();
4915    FSUtils.setFsDefault(conf, new Path(defaultFs));
4916    int ret = ToolRunner.run(new HBaseFsckTool(conf), args);
4917    System.exit(ret);
4918  }
4919
4920  /**
4921   * This is a Tool wrapper that gathers -Dxxx=yyy configuration settings from the command line.
4922   */
4923  static class HBaseFsckTool extends Configured implements Tool {
4924    HBaseFsckTool(Configuration conf) { super(conf); }
4925    @Override
4926    public int run(String[] args) throws Exception {
4927      HBaseFsck hbck = new HBaseFsck(getConf());
4928      hbck.exec(hbck.executor, args);
4929      hbck.close();
4930      return hbck.getRetCode();
4931    }
4932  };
4933
4934
4935  public HBaseFsck exec(ExecutorService exec, String[] args) throws KeeperException, IOException,
4936      InterruptedException {
4937    long sleepBeforeRerun = DEFAULT_SLEEP_BEFORE_RERUN;
4938
4939    boolean checkCorruptHFiles = false;
4940    boolean sidelineCorruptHFiles = false;
4941
4942    // Process command-line args.
4943    for (int i = 0; i < args.length; i++) {
4944      String cmd = args[i];
4945      if (cmd.equals("-help") || cmd.equals("-h")) {
4946        return printUsageAndExit();
4947      } else if (cmd.equals("-details")) {
4948        setDisplayFullReport();
4949      } else if (cmd.equals("-exclusive")) {
4950        setForceExclusive();
4951      } else if (cmd.equals("-timelag")) {
4952        if (i == args.length - 1) {
4953          errors.reportError(ERROR_CODE.WRONG_USAGE, "HBaseFsck: -timelag needs a value.");
4954          return printUsageAndExit();
4955        }
4956        try {
4957          long timelag = Long.parseLong(args[i+1]);
4958          setTimeLag(timelag);
4959        } catch (NumberFormatException e) {
4960          errors.reportError(ERROR_CODE.WRONG_USAGE, "-timelag needs a numeric value.");
4961          return printUsageAndExit();
4962        }
4963        i++;
4964      } else if (cmd.equals("-sleepBeforeRerun")) {
4965        if (i == args.length - 1) {
4966          errors.reportError(ERROR_CODE.WRONG_USAGE,
4967            "HBaseFsck: -sleepBeforeRerun needs a value.");
4968          return printUsageAndExit();
4969        }
4970        try {
4971          sleepBeforeRerun = Long.parseLong(args[i+1]);
4972        } catch (NumberFormatException e) {
4973          errors.reportError(ERROR_CODE.WRONG_USAGE, "-sleepBeforeRerun needs a numeric value.");
4974          return printUsageAndExit();
4975        }
4976        i++;
4977      } else if (cmd.equals("-sidelineDir")) {
4978        if (i == args.length - 1) {
4979          errors.reportError(ERROR_CODE.WRONG_USAGE, "HBaseFsck: -sidelineDir needs a value.");
4980          return printUsageAndExit();
4981        }
4982        i++;
4983        setSidelineDir(args[i]);
4984      } else if (cmd.equals("-fix")) {
4985        errors.reportError(ERROR_CODE.WRONG_USAGE,
4986          "This option is deprecated, please use  -fixAssignments instead.");
4987        setFixAssignments(true);
4988      } else if (cmd.equals("-fixAssignments")) {
4989        setFixAssignments(true);
4990      } else if (cmd.equals("-fixMeta")) {
4991        setFixMeta(true);
4992      } else if (cmd.equals("-noHdfsChecking")) {
4993        setCheckHdfs(false);
4994      } else if (cmd.equals("-fixHdfsHoles")) {
4995        setFixHdfsHoles(true);
4996      } else if (cmd.equals("-fixHdfsOrphans")) {
4997        setFixHdfsOrphans(true);
4998      } else if (cmd.equals("-fixTableOrphans")) {
4999        setFixTableOrphans(true);
5000      } else if (cmd.equals("-fixHdfsOverlaps")) {
5001        setFixHdfsOverlaps(true);
5002      } else if (cmd.equals("-fixVersionFile")) {
5003        setFixVersionFile(true);
5004      } else if (cmd.equals("-sidelineBigOverlaps")) {
5005        setSidelineBigOverlaps(true);
5006      } else if (cmd.equals("-fixSplitParents")) {
5007        setFixSplitParents(true);
5008      } else if (cmd.equals("-removeParents")) {
5009        setRemoveParents(true);
5010      } else if (cmd.equals("-ignorePreCheckPermission")) {
5011        setIgnorePreCheckPermission(true);
5012      } else if (cmd.equals("-checkCorruptHFiles")) {
5013        checkCorruptHFiles = true;
5014      } else if (cmd.equals("-sidelineCorruptHFiles")) {
5015        sidelineCorruptHFiles = true;
5016      } else if (cmd.equals("-fixReferenceFiles")) {
5017        setFixReferenceFiles(true);
5018      } else if (cmd.equals("-fixHFileLinks")) {
5019        setFixHFileLinks(true);
5020      } else if (cmd.equals("-fixEmptyMetaCells")) {
5021        setFixEmptyMetaCells(true);
5022      } else if (cmd.equals("-repair")) {
5023        // this attempts to merge overlapping hdfs regions, needs testing
5024        // under load
5025        setFixHdfsHoles(true);
5026        setFixHdfsOrphans(true);
5027        setFixMeta(true);
5028        setFixAssignments(true);
5029        setFixHdfsOverlaps(true);
5030        setFixVersionFile(true);
5031        setSidelineBigOverlaps(true);
5032        setFixSplitParents(false);
5033        setCheckHdfs(true);
5034        setFixReferenceFiles(true);
5035        setFixHFileLinks(true);
5036      } else if (cmd.equals("-repairHoles")) {
5037        // this will make all missing hdfs regions available but may lose data
5038        setFixHdfsHoles(true);
5039        setFixHdfsOrphans(false);
5040        setFixMeta(true);
5041        setFixAssignments(true);
5042        setFixHdfsOverlaps(false);
5043        setSidelineBigOverlaps(false);
5044        setFixSplitParents(false);
5045        setCheckHdfs(true);
5046      } else if (cmd.equals("-maxOverlapsToSideline")) {
5047        if (i == args.length - 1) {
5048          errors.reportError(ERROR_CODE.WRONG_USAGE,
5049            "-maxOverlapsToSideline needs a numeric value argument.");
5050          return printUsageAndExit();
5051        }
5052        try {
5053          int maxOverlapsToSideline = Integer.parseInt(args[i+1]);
5054          setMaxOverlapsToSideline(maxOverlapsToSideline);
5055        } catch (NumberFormatException e) {
5056          errors.reportError(ERROR_CODE.WRONG_USAGE,
5057            "-maxOverlapsToSideline needs a numeric value argument.");
5058          return printUsageAndExit();
5059        }
5060        i++;
5061      } else if (cmd.equals("-maxMerge")) {
5062        if (i == args.length - 1) {
5063          errors.reportError(ERROR_CODE.WRONG_USAGE,
5064            "-maxMerge needs a numeric value argument.");
5065          return printUsageAndExit();
5066        }
5067        try {
5068          int maxMerge = Integer.parseInt(args[i+1]);
5069          setMaxMerge(maxMerge);
5070        } catch (NumberFormatException e) {
5071          errors.reportError(ERROR_CODE.WRONG_USAGE,
5072            "-maxMerge needs a numeric value argument.");
5073          return printUsageAndExit();
5074        }
5075        i++;
5076      } else if (cmd.equals("-summary")) {
5077        setSummary();
5078      } else if (cmd.equals("-metaonly")) {
5079        setCheckMetaOnly();
5080      } else if (cmd.equals("-boundaries")) {
5081        setRegionBoundariesCheck();
5082      } else if (cmd.equals("-fixReplication")) {
5083        setFixReplication(true);
5084      } else if (cmd.startsWith("-")) {
5085        errors.reportError(ERROR_CODE.WRONG_USAGE, "Unrecognized option:" + cmd);
5086        return printUsageAndExit();
5087      } else {
5088        includeTable(TableName.valueOf(cmd));
5089        errors.print("Allow checking/fixes for table: " + cmd);
5090      }
5091    }
5092
5093    errors.print("HBaseFsck command line options: " + StringUtils.join(args, " "));
5094
5095    // pre-check current user has FS write permission or not
5096    try {
5097      preCheckPermission();
5098    } catch (AccessDeniedException ace) {
5099      Runtime.getRuntime().exit(-1);
5100    } catch (IOException ioe) {
5101      Runtime.getRuntime().exit(-1);
5102    }
5103
5104    // do the real work of hbck
5105    connect();
5106
5107    // after connecting to server above, we have server version
5108    // check if unsupported option is specified based on server version
5109    if (!isOptionsSupported(args)) {
5110      return printUsageAndExit();
5111    }
5112
5113    try {
5114      // if corrupt file mode is on, first fix them since they may be opened later
5115      if (checkCorruptHFiles || sidelineCorruptHFiles) {
5116        LOG.info("Checking all hfiles for corruption");
5117        HFileCorruptionChecker hfcc = createHFileCorruptionChecker(sidelineCorruptHFiles);
5118        setHFileCorruptionChecker(hfcc); // so we can get result
5119        Collection<TableName> tables = getIncludedTables();
5120        Collection<Path> tableDirs = new ArrayList<>();
5121        Path rootdir = FSUtils.getRootDir(getConf());
5122        if (tables.size() > 0) {
5123          for (TableName t : tables) {
5124            tableDirs.add(FSUtils.getTableDir(rootdir, t));
5125          }
5126        } else {
5127          tableDirs = FSUtils.getTableDirs(FSUtils.getCurrentFileSystem(getConf()), rootdir);
5128        }
5129        hfcc.checkTables(tableDirs);
5130        hfcc.report(errors);
5131      }
5132
5133      // check and fix table integrity, region consistency.
5134      int code = onlineHbck();
5135      setRetCode(code);
5136      // If we have changed the HBase state it is better to run hbck again
5137      // to see if we haven't broken something else in the process.
5138      // We run it only once more because otherwise we can easily fall into
5139      // an infinite loop.
5140      if (shouldRerun()) {
5141        try {
5142          LOG.info("Sleeping " + sleepBeforeRerun + "ms before re-checking after fix...");
5143          Thread.sleep(sleepBeforeRerun);
5144        } catch (InterruptedException ie) {
5145          LOG.warn("Interrupted while sleeping");
5146          return this;
5147        }
5148        // Just report
5149        setFixAssignments(false);
5150        setFixMeta(false);
5151        setFixHdfsHoles(false);
5152        setFixHdfsOverlaps(false);
5153        setFixVersionFile(false);
5154        setFixTableOrphans(false);
5155        errors.resetErrors();
5156        code = onlineHbck();
5157        setRetCode(code);
5158      }
5159    } finally {
5160      IOUtils.closeQuietly(this);
5161    }
5162    return this;
5163  }
5164
5165  private boolean isOptionsSupported(String[] args) {
5166    boolean result = true;
5167    String hbaseServerVersion = status.getHBaseVersion();
5168    Object[] versionComponents = VersionInfo.getVersionComponents(hbaseServerVersion);
5169    if (versionComponents[0] instanceof Integer && ((Integer)versionComponents[0]) >= 2) {
5170      // Process command-line args.
5171      for (String arg : args) {
5172        if (unsupportedOptionsInV2.contains(arg)) {
5173          errors.reportError(ERROR_CODE.UNSUPPORTED_OPTION,
5174              "option '" + arg + "' is not " + "supportted!");
5175          result = false;
5176          break;
5177        }
5178      }
5179    }
5180    return result;
5181  }
5182
5183  /**
5184   * ls -r for debugging purposes
5185   */
5186  void debugLsr(Path p) throws IOException {
5187    debugLsr(getConf(), p, errors);
5188  }
5189
5190  /**
5191   * ls -r for debugging purposes
5192   */
5193  public static void debugLsr(Configuration conf,
5194      Path p) throws IOException {
5195    debugLsr(conf, p, new PrintingErrorReporter());
5196  }
5197
5198  /**
5199   * ls -r for debugging purposes
5200   */
5201  public static void debugLsr(Configuration conf,
5202      Path p, ErrorReporter errors) throws IOException {
5203    if (!LOG.isDebugEnabled() || p == null) {
5204      return;
5205    }
5206    FileSystem fs = p.getFileSystem(conf);
5207
5208    if (!fs.exists(p)) {
5209      // nothing
5210      return;
5211    }
5212    errors.print(p.toString());
5213
5214    if (fs.isFile(p)) {
5215      return;
5216    }
5217
5218    if (fs.getFileStatus(p).isDirectory()) {
5219      FileStatus[] fss= fs.listStatus(p);
5220      for (FileStatus status : fss) {
5221        debugLsr(conf, status.getPath(), errors);
5222      }
5223    }
5224  }
5225}