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.Collection;
030import java.util.Collections;
031import java.util.Comparator;
032import java.util.EnumSet;
033import java.util.HashMap;
034import java.util.HashSet;
035import java.util.Iterator;
036import java.util.List;
037import java.util.Locale;
038import java.util.Map;
039import java.util.Map.Entry;
040import java.util.Objects;
041import java.util.Optional;
042import java.util.Set;
043import java.util.SortedMap;
044import java.util.TreeMap;
045import java.util.Vector;
046import java.util.concurrent.Callable;
047import java.util.concurrent.ConcurrentSkipListMap;
048import java.util.concurrent.ExecutionException;
049import java.util.concurrent.ExecutorService;
050import java.util.concurrent.Executors;
051import java.util.concurrent.Future;
052import java.util.concurrent.FutureTask;
053import java.util.concurrent.ScheduledThreadPoolExecutor;
054import java.util.concurrent.TimeUnit;
055import java.util.concurrent.TimeoutException;
056import java.util.concurrent.atomic.AtomicBoolean;
057import java.util.concurrent.atomic.AtomicInteger;
058import java.util.stream.Collectors;
059import org.apache.commons.io.IOUtils;
060import org.apache.commons.lang3.StringUtils;
061import org.apache.hadoop.conf.Configuration;
062import org.apache.hadoop.conf.Configured;
063import org.apache.hadoop.fs.FSDataOutputStream;
064import org.apache.hadoop.fs.FileStatus;
065import org.apache.hadoop.fs.FileSystem;
066import org.apache.hadoop.fs.Path;
067import org.apache.hadoop.fs.permission.FsAction;
068import org.apache.hadoop.fs.permission.FsPermission;
069import org.apache.hadoop.hbase.Abortable;
070import org.apache.hadoop.hbase.CatalogFamilyFormat;
071import org.apache.hadoop.hbase.Cell;
072import org.apache.hadoop.hbase.CellUtil;
073import org.apache.hadoop.hbase.ClientMetaTableAccessor;
074import org.apache.hadoop.hbase.ClusterMetrics;
075import org.apache.hadoop.hbase.ClusterMetrics.Option;
076import org.apache.hadoop.hbase.ExtendedCell;
077import org.apache.hadoop.hbase.HBaseConfiguration;
078import org.apache.hadoop.hbase.HBaseInterfaceAudience;
079import org.apache.hadoop.hbase.HConstants;
080import org.apache.hadoop.hbase.HRegionLocation;
081import org.apache.hadoop.hbase.KeyValue;
082import org.apache.hadoop.hbase.MasterNotRunningException;
083import org.apache.hadoop.hbase.MetaTableAccessor;
084import org.apache.hadoop.hbase.RegionLocations;
085import org.apache.hadoop.hbase.ServerName;
086import org.apache.hadoop.hbase.TableName;
087import org.apache.hadoop.hbase.TableNotFoundException;
088import org.apache.hadoop.hbase.ZooKeeperConnectionException;
089import org.apache.hadoop.hbase.client.Admin;
090import org.apache.hadoop.hbase.client.ColumnFamilyDescriptor;
091import org.apache.hadoop.hbase.client.ColumnFamilyDescriptorBuilder;
092import org.apache.hadoop.hbase.client.Connection;
093import org.apache.hadoop.hbase.client.ConnectionFactory;
094import org.apache.hadoop.hbase.client.Delete;
095import org.apache.hadoop.hbase.client.Get;
096import org.apache.hadoop.hbase.client.Put;
097import org.apache.hadoop.hbase.client.RegionInfo;
098import org.apache.hadoop.hbase.client.RegionInfoBuilder;
099import org.apache.hadoop.hbase.client.RegionLocator;
100import org.apache.hadoop.hbase.client.RegionReplicaUtil;
101import org.apache.hadoop.hbase.client.Result;
102import org.apache.hadoop.hbase.client.ResultScanner;
103import org.apache.hadoop.hbase.client.RowMutations;
104import org.apache.hadoop.hbase.client.Scan;
105import org.apache.hadoop.hbase.client.Table;
106import org.apache.hadoop.hbase.client.TableDescriptor;
107import org.apache.hadoop.hbase.client.TableDescriptorBuilder;
108import org.apache.hadoop.hbase.client.TableState;
109import org.apache.hadoop.hbase.io.FileLink;
110import org.apache.hadoop.hbase.io.HFileLink;
111import org.apache.hadoop.hbase.io.hfile.CacheConfig;
112import org.apache.hadoop.hbase.io.hfile.HFile;
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.replication.ReplicationException;
118import org.apache.hadoop.hbase.replication.ReplicationPeerDescription;
119import org.apache.hadoop.hbase.replication.ReplicationQueueStorage;
120import org.apache.hadoop.hbase.replication.ReplicationStorageFactory;
121import org.apache.hadoop.hbase.security.UserProvider;
122import org.apache.hadoop.hbase.util.Bytes.ByteArrayComparator;
123import org.apache.hadoop.hbase.util.HbckErrorReporter.ERROR_CODE;
124import org.apache.hadoop.hbase.util.hbck.HFileCorruptionChecker;
125import org.apache.hadoop.hbase.util.hbck.ReplicationChecker;
126import org.apache.hadoop.hbase.util.hbck.TableIntegrityErrorHandler;
127import org.apache.hadoop.hbase.wal.WALSplitUtil;
128import org.apache.hadoop.hbase.zookeeper.ZKUtil;
129import org.apache.hadoop.hbase.zookeeper.ZKWatcher;
130import org.apache.hadoop.hbase.zookeeper.ZNodePaths;
131import org.apache.hadoop.hdfs.protocol.AlreadyBeingCreatedException;
132import org.apache.hadoop.ipc.RemoteException;
133import org.apache.hadoop.security.AccessControlException;
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.yetus.audience.InterfaceAudience;
139import org.apache.yetus.audience.InterfaceStability;
140import org.apache.zookeeper.KeeperException;
141import org.slf4j.Logger;
142import org.slf4j.LoggerFactory;
143
144import org.apache.hbase.thirdparty.com.google.common.base.Joiner;
145import org.apache.hbase.thirdparty.com.google.common.base.Preconditions;
146import org.apache.hbase.thirdparty.com.google.common.collect.Lists;
147import org.apache.hbase.thirdparty.com.google.common.collect.Sets;
148import org.apache.hbase.thirdparty.com.google.common.io.Closeables;
149import org.apache.hbase.thirdparty.com.google.common.util.concurrent.ThreadFactoryBuilder;
150
151/**
152 * HBaseFsck (hbck) is a tool for checking and repairing region consistency and table integrity
153 * problems in a corrupted HBase. This tool was written for hbase-1.x. It does not work with
154 * hbase-2.x; it can read state but is not allowed to change state; i.e. effect 'repair'. Even
155 * though it can 'read' state, given how so much has changed in how hbase1 and hbase2 operate, it
156 * will often misread. See hbck2 (HBASE-19121) for a hbck tool for hbase2. This class is deprecated.
157 * <p>
158 * Region consistency checks verify that hbase:meta, region deployment on region servers and the
159 * state of data in HDFS (.regioninfo files) all are in accordance.
160 * <p>
161 * Table integrity checks verify that all possible row keys resolve to exactly one region of a
162 * table. This means there are no individual degenerate or backwards regions; no holes between
163 * regions; and that there are no overlapping regions.
164 * <p>
165 * The general repair strategy works in two phases:
166 * <ol>
167 * <li>Repair Table Integrity on HDFS. (merge or fabricate regions)
168 * <li>Repair Region Consistency with hbase:meta and assignments
169 * </ol>
170 * <p>
171 * For table integrity repairs, the tables' region directories are scanned for .regioninfo files.
172 * Each table's integrity is then verified. If there are any orphan regions (regions with no
173 * .regioninfo files) or holes, new regions are fabricated. Backwards regions are sidelined as well
174 * as empty degenerate (endkey==startkey) regions. If there are any overlapping regions, a new
175 * region is created and all data is merged into the new region.
176 * <p>
177 * Table integrity repairs deal solely with HDFS and could potentially be done offline -- the hbase
178 * region servers or master do not need to be running. This phase can eventually be used to
179 * completely reconstruct the hbase:meta table in an offline fashion.
180 * <p>
181 * Region consistency requires three conditions -- 1) valid .regioninfo file present in an HDFS
182 * region dir, 2) valid row with .regioninfo data in META, and 3) a region is deployed only at the
183 * regionserver that was assigned to with proper state in the master.
184 * <p>
185 * Region consistency repairs require hbase to be online so that hbck can contact the HBase master
186 * and region servers. The hbck#connect() method must first be called successfully. Much of the
187 * region consistency information is transient and less risky to repair.
188 * <p>
189 * If hbck is run from the command line, there are a handful of arguments that can be used to limit
190 * the kinds of repairs hbck will do. See the code in {@link #printUsageAndExit()} for more details.
191 * @deprecated For removal in hbase-4.0.0. Use HBCK2 instead.
192 */
193@Deprecated
194@InterfaceAudience.LimitedPrivate(HBaseInterfaceAudience.TOOLS)
195@InterfaceStability.Evolving
196public class HBaseFsck extends Configured implements Closeable {
197  public static final long DEFAULT_TIME_LAG = 60000; // default value of 1 minute
198  public static final long DEFAULT_SLEEP_BEFORE_RERUN = 10000;
199  private static final int MAX_NUM_THREADS = 50; // #threads to contact regions
200  private static boolean rsSupportsOffline = true;
201  private static final int DEFAULT_OVERLAPS_TO_SIDELINE = 2;
202  private static final int DEFAULT_MAX_MERGE = 5;
203
204  /**
205   * Here is where hbase-1.x used to default the lock for hbck1. It puts in place a lock when it
206   * goes to write/make changes.
207   */
208  @InterfaceAudience.Private
209  public static final String HBCK_LOCK_FILE = "hbase-hbck.lock";
210  private static final int DEFAULT_MAX_LOCK_FILE_ATTEMPTS = 5;
211  private static final int DEFAULT_LOCK_FILE_ATTEMPT_SLEEP_INTERVAL = 200; // milliseconds
212  private static final int DEFAULT_LOCK_FILE_ATTEMPT_MAX_SLEEP_TIME = 5000; // milliseconds
213  // We have to set the timeout value > HdfsConstants.LEASE_SOFTLIMIT_PERIOD.
214  // In HADOOP-2.6 and later, the Namenode proxy now created with custom RetryPolicy for
215  // AlreadyBeingCreatedException which is implies timeout on this operations up to
216  // HdfsConstants.LEASE_SOFTLIMIT_PERIOD (60 seconds).
217  private static final int DEFAULT_WAIT_FOR_LOCK_TIMEOUT = 80; // seconds
218  private static final int DEFAULT_MAX_CREATE_ZNODE_ATTEMPTS = 5;
219  private static final int DEFAULT_CREATE_ZNODE_ATTEMPT_SLEEP_INTERVAL = 200; // milliseconds
220  private static final int DEFAULT_CREATE_ZNODE_ATTEMPT_MAX_SLEEP_TIME = 5000; // milliseconds
221
222  /**********************
223   * Internal resources
224   **********************/
225  private static final Logger LOG = LoggerFactory.getLogger(HBaseFsck.class.getName());
226  private ClusterMetrics status;
227  private Connection connection;
228  private Admin admin;
229  private Table meta;
230  // threads to do ||izable tasks: retrieve data from regionservers, handle overlapping regions
231  protected ExecutorService executor;
232  private long startMillis = EnvironmentEdgeManager.currentTime();
233  private HFileCorruptionChecker hfcc;
234  private int retcode = 0;
235  private Path HBCK_LOCK_PATH;
236  private FSDataOutputStream hbckOutFd;
237  // This lock is to prevent cleanup of balancer resources twice between
238  // ShutdownHook and the main code. We cleanup only if the connect() is
239  // successful
240  private final AtomicBoolean hbckLockCleanup = new AtomicBoolean(false);
241
242  // Unsupported options in HBase 2.0+
243  private static final Set<String> unsupportedOptionsInV2 = Sets.newHashSet("-fix",
244    "-fixAssignments", "-fixMeta", "-fixHdfsHoles", "-fixHdfsOrphans", "-fixTableOrphans",
245    "-fixHdfsOverlaps", "-sidelineBigOverlaps", "-fixSplitParents", "-removeParents",
246    "-fixEmptyMetaCells", "-repair", "-repairHoles", "-maxOverlapsToSideline", "-maxMerge");
247
248  /***********
249   * Options
250   ***********/
251  private static boolean details = false; // do we display the full report
252  private long timelag = DEFAULT_TIME_LAG; // tables whose modtime is older
253  private static boolean forceExclusive = false; // only this hbck can modify HBase
254  private boolean fixAssignments = false; // fix assignment errors?
255  private boolean fixMeta = false; // fix meta errors?
256  private boolean checkHdfs = true; // load and check fs consistency?
257  private boolean fixHdfsHoles = false; // fix fs holes?
258  private boolean fixHdfsOverlaps = false; // fix fs overlaps (risky)
259  private boolean fixHdfsOrphans = false; // fix fs holes (missing .regioninfo)
260  private boolean fixTableOrphans = false; // fix fs holes (missing .tableinfo)
261  private boolean fixVersionFile = false; // fix missing hbase.version file in hdfs
262  private boolean fixSplitParents = false; // fix lingering split parents
263  private boolean removeParents = false; // remove split parents
264  private boolean fixReferenceFiles = false; // fix lingering reference store file
265  private boolean fixHFileLinks = false; // fix lingering HFileLinks
266  private boolean fixEmptyMetaCells = false; // fix (remove) empty REGIONINFO_QUALIFIER rows
267  private boolean fixReplication = false; // fix undeleted replication queues for removed peer
268  private boolean cleanReplicationBarrier = false; // clean replication barriers of a table
269  private boolean fixAny = false; // Set to true if any of the fix is required.
270
271  // limit checking/fixes to listed tables, if empty attempt to check/fix all
272  // hbase:meta are always checked
273  private Set<TableName> tablesIncluded = new HashSet<>();
274  private TableName cleanReplicationBarrierTable;
275  private int maxMerge = DEFAULT_MAX_MERGE; // maximum number of overlapping regions to merge
276  // maximum number of overlapping regions to sideline
277  private int maxOverlapsToSideline = DEFAULT_OVERLAPS_TO_SIDELINE;
278  private boolean sidelineBigOverlaps = false; // sideline overlaps with >maxMerge regions
279  private Path sidelineDir = null;
280
281  private boolean rerun = false; // if we tried to fix something, rerun hbck
282  private static boolean summary = false; // if we want to print less output
283  private boolean checkMetaOnly = false;
284  private boolean checkRegionBoundaries = false;
285  private boolean ignorePreCheckPermission = false; // if pre-check permission
286
287  /*********
288   * State
289   *********/
290  final private HbckErrorReporter errors;
291  int fixes = 0;
292
293  /**
294   * This map contains the state of all hbck items. It maps from encoded region name to
295   * HbckRegionInfo structure. The information contained in HbckRegionInfo is used to detect and
296   * correct consistency (hdfs/meta/deployment) problems.
297   */
298  private TreeMap<String, HbckRegionInfo> regionInfoMap = new TreeMap<>();
299  // Empty regioninfo qualifiers in hbase:meta
300  private Set<Result> emptyRegionInfoQualifiers = new HashSet<>();
301
302  /**
303   * This map from Tablename -> TableInfo contains the structures necessary to detect table
304   * consistency problems (holes, dupes, overlaps). It is sorted to prevent dupes. If tablesIncluded
305   * is empty, this map contains all tables. Otherwise, it contains only meta tables and tables in
306   * tablesIncluded, unless checkMetaOnly is specified, in which case, it contains only the meta
307   * table
308   */
309  private SortedMap<TableName, HbckTableInfo> tablesInfo = new ConcurrentSkipListMap<>();
310
311  /**
312   * When initially looking at HDFS, we attempt to find any orphaned data.
313   */
314  private List<HbckRegionInfo> orphanHdfsDirs = Collections.synchronizedList(new ArrayList<>());
315
316  private Map<TableName, Set<String>> orphanTableDirs = new HashMap<>();
317  private Map<TableName, TableState> tableStates = new HashMap<>();
318  private final RetryCounterFactory lockFileRetryCounterFactory;
319  private final RetryCounterFactory createZNodeRetryCounterFactory;
320
321  private Map<TableName, Set<String>> skippedRegions = new HashMap<>();
322
323  private ZKWatcher zkw = null;
324  private String hbckEphemeralNodePath = null;
325  private boolean hbckZodeCreated = false;
326
327  /**
328   * Constructor
329   * @param conf Configuration object
330   * @throws MasterNotRunningException    if the master is not running
331   * @throws ZooKeeperConnectionException if unable to connect to ZooKeeper
332   */
333  public HBaseFsck(Configuration conf) throws IOException, ClassNotFoundException {
334    this(conf, createThreadPool(conf));
335  }
336
337  private static ExecutorService createThreadPool(Configuration conf) {
338    int numThreads = conf.getInt("hbasefsck.numthreads", MAX_NUM_THREADS);
339    return new ScheduledThreadPoolExecutor(numThreads,
340      new ThreadFactoryBuilder().setNameFormat("hbasefsck-pool-%d").setDaemon(true)
341        .setUncaughtExceptionHandler(Threads.LOGGING_EXCEPTION_HANDLER).build());
342  }
343
344  /**
345   * Constructor Configuration object if the master is not running if unable to connect to ZooKeeper
346   */
347  public HBaseFsck(Configuration conf, ExecutorService exec) throws MasterNotRunningException,
348    ZooKeeperConnectionException, IOException, ClassNotFoundException {
349    super(conf);
350    errors = getErrorReporter(getConf());
351    this.executor = exec;
352    lockFileRetryCounterFactory = createLockRetryCounterFactory(getConf());
353    createZNodeRetryCounterFactory = createZnodeRetryCounterFactory(getConf());
354    zkw = createZooKeeperWatcher();
355  }
356
357  /** Returns A retry counter factory configured for retrying lock file creation. */
358  public static RetryCounterFactory createLockRetryCounterFactory(Configuration conf) {
359    return new RetryCounterFactory(
360      conf.getInt("hbase.hbck.lockfile.attempts", DEFAULT_MAX_LOCK_FILE_ATTEMPTS),
361      conf.getInt("hbase.hbck.lockfile.attempt.sleep.interval",
362        DEFAULT_LOCK_FILE_ATTEMPT_SLEEP_INTERVAL),
363      conf.getInt("hbase.hbck.lockfile.attempt.maxsleeptime",
364        DEFAULT_LOCK_FILE_ATTEMPT_MAX_SLEEP_TIME));
365  }
366
367  /** Returns A retry counter factory configured for retrying znode creation. */
368  private static RetryCounterFactory createZnodeRetryCounterFactory(Configuration conf) {
369    return new RetryCounterFactory(
370      conf.getInt("hbase.hbck.createznode.attempts", DEFAULT_MAX_CREATE_ZNODE_ATTEMPTS),
371      conf.getInt("hbase.hbck.createznode.attempt.sleep.interval",
372        DEFAULT_CREATE_ZNODE_ATTEMPT_SLEEP_INTERVAL),
373      conf.getInt("hbase.hbck.createznode.attempt.maxsleeptime",
374        DEFAULT_CREATE_ZNODE_ATTEMPT_MAX_SLEEP_TIME));
375  }
376
377  /** Returns Return the tmp dir this tool writes too. */
378  @InterfaceAudience.Private
379  public static Path getTmpDir(Configuration conf) throws IOException {
380    return new Path(CommonFSUtils.getRootDir(conf), HConstants.HBASE_TEMP_DIRECTORY);
381  }
382
383  private static class FileLockCallable implements Callable<FSDataOutputStream> {
384    RetryCounter retryCounter;
385    private final Configuration conf;
386    private Path hbckLockPath = null;
387
388    public FileLockCallable(Configuration conf, RetryCounter retryCounter) {
389      this.retryCounter = retryCounter;
390      this.conf = conf;
391    }
392
393    /** Returns Will be <code>null</code> unless you call {@link #call()} */
394    Path getHbckLockPath() {
395      return this.hbckLockPath;
396    }
397
398    @Override
399    public FSDataOutputStream call() throws IOException {
400      try {
401        FileSystem fs = CommonFSUtils.getCurrentFileSystem(this.conf);
402        FsPermission defaultPerms =
403          CommonFSUtils.getFilePermissions(fs, this.conf, HConstants.DATA_FILE_UMASK_KEY);
404        Path tmpDir = getTmpDir(conf);
405        this.hbckLockPath = new Path(tmpDir, HBCK_LOCK_FILE);
406        fs.mkdirs(tmpDir);
407        final FSDataOutputStream out = createFileWithRetries(fs, this.hbckLockPath, defaultPerms);
408        out.writeBytes(InetAddress.getLocalHost().toString());
409        // Add a note into the file we write on why hbase2 is writing out an hbck1 lock file.
410        out.writeBytes(" Written by an hbase-2.x Master to block an "
411          + "attempt by an hbase-1.x HBCK tool making modification to state. "
412          + "See 'HBCK must match HBase server version' in the hbase refguide.");
413        out.flush();
414        return out;
415      } catch (RemoteException e) {
416        if (AlreadyBeingCreatedException.class.getName().equals(e.getClassName())) {
417          return null;
418        } else {
419          throw e;
420        }
421      }
422    }
423
424    private FSDataOutputStream createFileWithRetries(final FileSystem fs,
425      final Path hbckLockFilePath, final FsPermission defaultPerms) throws IOException {
426      IOException exception = null;
427      do {
428        try {
429          return CommonFSUtils.create(fs, hbckLockFilePath, defaultPerms, false);
430        } catch (IOException ioe) {
431          LOG.info("Failed to create lock file " + hbckLockFilePath.getName() + ", try="
432            + (retryCounter.getAttemptTimes() + 1) + " of " + retryCounter.getMaxAttempts());
433          LOG.debug("Failed to create lock file " + hbckLockFilePath.getName(), ioe);
434          try {
435            exception = ioe;
436            retryCounter.sleepUntilNextRetry();
437          } catch (InterruptedException ie) {
438            throw (InterruptedIOException) new InterruptedIOException(
439              "Can't create lock file " + hbckLockFilePath.getName()).initCause(ie);
440          }
441        }
442      } while (retryCounter.shouldRetry());
443
444      throw exception;
445    }
446  }
447
448  /**
449   * This method maintains a lock using a file. If the creation fails we return null
450   * @return FSDataOutputStream object corresponding to the newly opened lock file
451   * @throws IOException if IO failure occurs
452   */
453  public static Pair<Path, FSDataOutputStream> checkAndMarkRunningHbck(Configuration conf,
454    RetryCounter retryCounter) throws IOException {
455    FileLockCallable callable = new FileLockCallable(conf, retryCounter);
456    ExecutorService executor = Executors.newFixedThreadPool(1);
457    FutureTask<FSDataOutputStream> futureTask = new FutureTask<>(callable);
458    executor.execute(futureTask);
459    final int timeoutInSeconds =
460      conf.getInt("hbase.hbck.lockfile.maxwaittime", DEFAULT_WAIT_FOR_LOCK_TIMEOUT);
461    FSDataOutputStream stream = null;
462    try {
463      stream = futureTask.get(timeoutInSeconds, TimeUnit.SECONDS);
464    } catch (ExecutionException ee) {
465      LOG.warn("Encountered exception when opening lock file", ee);
466    } catch (InterruptedException ie) {
467      LOG.warn("Interrupted when opening lock file", ie);
468      Thread.currentThread().interrupt();
469    } catch (TimeoutException exception) {
470      // took too long to obtain lock
471      LOG.warn("Took more than " + timeoutInSeconds + " seconds in obtaining lock");
472      futureTask.cancel(true);
473    } finally {
474      executor.shutdownNow();
475    }
476    return new Pair<Path, FSDataOutputStream>(callable.getHbckLockPath(), stream);
477  }
478
479  private void unlockHbck() {
480    if (isExclusive() && hbckLockCleanup.compareAndSet(true, false)) {
481      RetryCounter retryCounter = lockFileRetryCounterFactory.create();
482      do {
483        try {
484          Closeables.close(hbckOutFd, true);
485          CommonFSUtils.delete(CommonFSUtils.getCurrentFileSystem(getConf()), HBCK_LOCK_PATH, true);
486          LOG.info("Finishing hbck");
487          return;
488        } catch (IOException ioe) {
489          LOG.info("Failed to delete " + HBCK_LOCK_PATH + ", try="
490            + (retryCounter.getAttemptTimes() + 1) + " of " + retryCounter.getMaxAttempts());
491          LOG.debug("Failed to delete " + HBCK_LOCK_PATH, ioe);
492          try {
493            retryCounter.sleepUntilNextRetry();
494          } catch (InterruptedException ie) {
495            Thread.currentThread().interrupt();
496            LOG.warn("Interrupted while deleting lock file" + HBCK_LOCK_PATH);
497            return;
498          }
499        }
500      } while (retryCounter.shouldRetry());
501    }
502  }
503
504  /**
505   * To repair region consistency, one must call connect() in order to repair online state.
506   */
507  public void connect() throws IOException {
508
509    if (isExclusive()) {
510      // Grab the lock
511      Pair<Path, FSDataOutputStream> pair =
512        checkAndMarkRunningHbck(getConf(), this.lockFileRetryCounterFactory.create());
513      HBCK_LOCK_PATH = pair.getFirst();
514      this.hbckOutFd = pair.getSecond();
515      if (hbckOutFd == null) {
516        setRetCode(-1);
517        LOG.error("Another instance of hbck is fixing HBase, exiting this instance. "
518          + "[If you are sure no other instance is running, delete the lock file " + HBCK_LOCK_PATH
519          + " and rerun the tool]");
520        throw new IOException("Duplicate hbck - Abort");
521      }
522
523      // Make sure to cleanup the lock
524      hbckLockCleanup.set(true);
525    }
526
527    // Add a shutdown hook to this thread, in case user tries to
528    // kill the hbck with a ctrl-c, we want to cleanup the lock so that
529    // it is available for further calls
530    Runtime.getRuntime().addShutdownHook(new Thread() {
531      @Override
532      public void run() {
533        IOUtils.closeQuietly(HBaseFsck.this, e -> LOG.warn("", e));
534        cleanupHbckZnode();
535        unlockHbck();
536      }
537    });
538
539    LOG.info("Launching hbck");
540
541    connection = ConnectionFactory.createConnection(getConf());
542    admin = connection.getAdmin();
543    meta = connection.getTable(TableName.META_TABLE_NAME);
544    status = admin.getClusterMetrics(EnumSet.of(Option.LIVE_SERVERS, Option.DEAD_SERVERS,
545      Option.MASTER, Option.BACKUP_MASTERS, Option.REGIONS_IN_TRANSITION, Option.HBASE_VERSION));
546  }
547
548  /**
549   * Get deployed regions according to the region servers.
550   */
551  private void loadDeployedRegions() throws IOException, InterruptedException {
552    // From the master, get a list of all known live region servers
553    Collection<ServerName> regionServers = status.getLiveServerMetrics().keySet();
554    errors.print("Number of live region servers: " + regionServers.size());
555    if (details) {
556      for (ServerName rsinfo : regionServers) {
557        errors.print("  " + rsinfo.getServerName());
558      }
559    }
560
561    // From the master, get a list of all dead region servers
562    Collection<ServerName> deadRegionServers = status.getDeadServerNames();
563    errors.print("Number of dead region servers: " + deadRegionServers.size());
564    if (details) {
565      for (ServerName name : deadRegionServers) {
566        errors.print("  " + name);
567      }
568    }
569
570    // Print the current master name and state
571    errors.print("Master: " + status.getMasterName());
572
573    // Print the list of all backup masters
574    Collection<ServerName> backupMasters = status.getBackupMasterNames();
575    errors.print("Number of backup masters: " + backupMasters.size());
576    if (details) {
577      for (ServerName name : backupMasters) {
578        errors.print("  " + name);
579      }
580    }
581
582    errors.print("Average load: " + status.getAverageLoad());
583    errors.print("Number of requests: " + status.getRequestCount());
584    errors.print("Number of regions: " + status.getRegionCount());
585
586    List<RegionState> rits = status.getRegionStatesInTransition();
587    errors.print("Number of regions in transition: " + rits.size());
588    if (details) {
589      for (RegionState state : rits) {
590        errors.print("  " + state.toDescriptiveString());
591      }
592    }
593
594    // Determine what's deployed
595    processRegionServers(regionServers);
596  }
597
598  /**
599   * Clear the current state of hbck.
600   */
601  private void clearState() {
602    // Make sure regionInfo is empty before starting
603    fixes = 0;
604    regionInfoMap.clear();
605    emptyRegionInfoQualifiers.clear();
606    tableStates.clear();
607    errors.clear();
608    tablesInfo.clear();
609    orphanHdfsDirs.clear();
610    skippedRegions.clear();
611  }
612
613  /**
614   * This repair method analyzes hbase data in hdfs and repairs it to satisfy the table integrity
615   * rules. HBase doesn't need to be online for this operation to work.
616   */
617  public void offlineHdfsIntegrityRepair() throws IOException, InterruptedException {
618    // Initial pass to fix orphans.
619    if (
620      shouldCheckHdfs() && (shouldFixHdfsOrphans() || shouldFixHdfsHoles()
621        || shouldFixHdfsOverlaps() || shouldFixTableOrphans())
622    ) {
623      LOG.info("Loading regioninfos HDFS");
624      // if nothing is happening this should always complete in two iterations.
625      int maxIterations = getConf().getInt("hbase.hbck.integrityrepair.iterations.max", 3);
626      int curIter = 0;
627      do {
628        clearState(); // clears hbck state and reset fixes to 0 and.
629        // repair what's on HDFS
630        restoreHdfsIntegrity();
631        curIter++;// limit the number of iterations.
632      } while (fixes > 0 && curIter <= maxIterations);
633
634      // Repairs should be done in the first iteration and verification in the second.
635      // If there are more than 2 passes, something funny has happened.
636      if (curIter > 2) {
637        if (curIter == maxIterations) {
638          LOG.warn("Exiting integrity repairs after max " + curIter + " iterations. "
639            + "Tables integrity may not be fully repaired!");
640        } else {
641          LOG.info("Successfully exiting integrity repairs after " + curIter + " iterations");
642        }
643      }
644    }
645  }
646
647  /**
648   * This repair method requires the cluster to be online since it contacts region servers and the
649   * masters. It makes each region's state in HDFS, in hbase:meta, and deployments consistent.
650   * @return If &gt; 0 , number of errors detected, if &lt; 0 there was an unrecoverable error. If
651   *         0, we have a clean hbase.
652   */
653  public int onlineConsistencyRepair() throws IOException, KeeperException, InterruptedException {
654
655    // get regions according to what is online on each RegionServer
656    loadDeployedRegions();
657    // check whether hbase:meta is deployed and online
658    recordMetaRegion();
659    // Report inconsistencies if there are any unknown servers.
660    reportUnknownServers();
661    // Check if hbase:meta is found only once and in the right place
662    if (!checkMetaRegion()) {
663      String errorMsg = TableName.META_TABLE_NAME + " table is not consistent. ";
664      if (shouldFixAssignments()) {
665        errorMsg += "HBCK will try fixing it. Rerun once " + TableName.META_TABLE_NAME + " is back "
666          + "to consistent state.";
667      } else {
668        errorMsg += "Run HBCK with proper fix options to fix " + TableName.META_TABLE_NAME
669          + " inconsistency.";
670      }
671      errors.reportError(errorMsg + " Exiting...");
672      return -2;
673    }
674    // Not going with further consistency check for tables when hbase:meta itself is not consistent.
675    LOG.info("Loading regionsinfo from the {} table", TableName.META_TABLE_NAME);
676    boolean success = loadMetaEntries();
677    if (!success) return -1;
678
679    // Empty cells in hbase:meta?
680    reportEmptyMetaCells();
681
682    // Check if we have to cleanup empty REGIONINFO_QUALIFIER rows from hbase:meta
683    if (shouldFixEmptyMetaCells()) {
684      fixEmptyMetaCells();
685    }
686
687    // get a list of all tables that have not changed recently.
688    if (!checkMetaOnly) {
689      reportTablesInFlux();
690    }
691
692    // Get disabled tables states
693    loadTableStates();
694
695    // load regiondirs and regioninfos from HDFS
696    if (shouldCheckHdfs()) {
697      LOG.info("Loading region directories from HDFS");
698      loadHdfsRegionDirs();
699      LOG.info("Loading region information from HDFS");
700      loadHdfsRegionInfos();
701    }
702
703    // fix the orphan tables
704    fixOrphanTables();
705
706    LOG.info("Checking and fixing region consistency");
707    // Check and fix consistency
708    checkAndFixConsistency();
709
710    // Check integrity (does not fix)
711    checkIntegrity();
712    return errors.getErrorList().size();
713  }
714
715  private void reportUnknownServers() throws IOException {
716    List<ServerName> unknownServers = admin.listUnknownServers();
717    if (!unknownServers.isEmpty()) {
718      unknownServers.stream().forEach(serverName -> {
719        errors.reportError(ERROR_CODE.UNKNOWN_SERVER,
720          "Found unknown server, some of the regions held by this server may not get assigned. "
721            + String.format("Use HBCK2 scheduleRecoveries %s to recover.", serverName));
722      });
723    }
724  }
725
726  /**
727   * This method maintains an ephemeral znode. If the creation fails we return false or throw
728   * exception
729   * @return true if creating znode succeeds; false otherwise
730   * @throws IOException if IO failure occurs
731   */
732  private boolean setMasterInMaintenanceMode() throws IOException {
733    RetryCounter retryCounter = createZNodeRetryCounterFactory.create();
734    hbckEphemeralNodePath = ZNodePaths.joinZNode(zkw.getZNodePaths().masterMaintZNode,
735      "hbck-" + Long.toString(EnvironmentEdgeManager.currentTime()));
736    do {
737      try {
738        hbckZodeCreated = ZKUtil.createEphemeralNodeAndWatch(zkw, hbckEphemeralNodePath, null);
739        if (hbckZodeCreated) {
740          break;
741        }
742      } catch (KeeperException e) {
743        if (retryCounter.getAttemptTimes() >= retryCounter.getMaxAttempts()) {
744          throw new IOException("Can't create znode " + hbckEphemeralNodePath, e);
745        }
746        // fall through and retry
747      }
748
749      LOG.warn("Fail to create znode " + hbckEphemeralNodePath + ", try="
750        + (retryCounter.getAttemptTimes() + 1) + " of " + retryCounter.getMaxAttempts());
751
752      try {
753        retryCounter.sleepUntilNextRetry();
754      } catch (InterruptedException ie) {
755        throw (InterruptedIOException) new InterruptedIOException(
756          "Can't create znode " + hbckEphemeralNodePath).initCause(ie);
757      }
758    } while (retryCounter.shouldRetry());
759    return hbckZodeCreated;
760  }
761
762  private void cleanupHbckZnode() {
763    try {
764      if (zkw != null && hbckZodeCreated) {
765        ZKUtil.deleteNode(zkw, hbckEphemeralNodePath);
766        hbckZodeCreated = false;
767      }
768    } catch (KeeperException e) {
769      // Ignore
770      if (!e.code().equals(KeeperException.Code.NONODE)) {
771        LOG.warn("Delete HBCK znode " + hbckEphemeralNodePath + " failed ", e);
772      }
773    }
774  }
775
776  /**
777   * Contacts the master and prints out cluster-wide information
778   * @return 0 on success, non-zero on failure
779   */
780  public int onlineHbck()
781    throws IOException, KeeperException, InterruptedException, ReplicationException {
782    // print hbase server version
783    errors.print("Version: " + status.getHBaseVersion());
784
785    // Clean start
786    clearState();
787    // Do offline check and repair first
788    offlineHdfsIntegrityRepair();
789    offlineReferenceFileRepair();
790    offlineHLinkFileRepair();
791    // If Master runs maintenance tasks (such as balancer, catalog janitor, etc) during online
792    // hbck, it is likely that hbck would be misled and report transient errors. Therefore, it
793    // is better to set Master into maintenance mode during online hbck.
794    //
795    if (!setMasterInMaintenanceMode()) {
796      LOG.warn("HBCK is running while master is not in maintenance mode, you might see transient "
797        + "error.  Please run HBCK multiple times to reduce the chance of transient error.");
798    }
799
800    onlineConsistencyRepair();
801
802    if (checkRegionBoundaries) {
803      checkRegionBoundaries();
804    }
805
806    checkAndFixReplication();
807
808    cleanReplicationBarrier();
809
810    // Remove the hbck znode
811    cleanupHbckZnode();
812
813    // Remove the hbck lock
814    unlockHbck();
815
816    // Print table summary
817    printTableSummary(tablesInfo);
818    return errors.summarize();
819  }
820
821  public static byte[] keyOnly(byte[] b) {
822    if (b == null) return b;
823    int rowlength = Bytes.toShort(b, 0);
824    byte[] result = new byte[rowlength];
825    System.arraycopy(b, Bytes.SIZEOF_SHORT, result, 0, rowlength);
826    return result;
827  }
828
829  @Override
830  public void close() throws IOException {
831    try {
832      cleanupHbckZnode();
833      unlockHbck();
834    } catch (Exception io) {
835      LOG.warn(io.toString(), io);
836    } finally {
837      if (zkw != null) {
838        zkw.close();
839        zkw = null;
840      }
841      IOUtils.closeQuietly(admin, e -> LOG.warn("", e));
842      IOUtils.closeQuietly(meta, e -> LOG.warn("", e));
843      IOUtils.closeQuietly(connection, e -> LOG.warn("", e));
844    }
845  }
846
847  private static class RegionBoundariesInformation {
848    public byte[] regionName;
849    public byte[] metaFirstKey;
850    public byte[] metaLastKey;
851    public byte[] storesFirstKey;
852    public byte[] storesLastKey;
853
854    @Override
855    public String toString() {
856      return "regionName=" + Bytes.toStringBinary(regionName) + "\nmetaFirstKey="
857        + Bytes.toStringBinary(metaFirstKey) + "\nmetaLastKey=" + Bytes.toStringBinary(metaLastKey)
858        + "\nstoresFirstKey=" + Bytes.toStringBinary(storesFirstKey) + "\nstoresLastKey="
859        + Bytes.toStringBinary(storesLastKey);
860    }
861  }
862
863  public void checkRegionBoundaries() {
864    try {
865      ByteArrayComparator comparator = new ByteArrayComparator();
866      List<RegionInfo> regions = MetaTableAccessor.getAllRegions(connection, true);
867      final RegionBoundariesInformation currentRegionBoundariesInformation =
868        new RegionBoundariesInformation();
869      Path hbaseRoot = CommonFSUtils.getRootDir(getConf());
870      for (RegionInfo regionInfo : regions) {
871        Path tableDir = CommonFSUtils.getTableDir(hbaseRoot, regionInfo.getTable());
872        currentRegionBoundariesInformation.regionName = regionInfo.getRegionName();
873        // For each region, get the start and stop key from the META and compare them to the
874        // same information from the Stores.
875        Path path = new Path(tableDir, regionInfo.getEncodedName());
876        FileSystem fs = path.getFileSystem(getConf());
877        FileStatus[] files = fs.listStatus(path);
878        // For all the column families in this region...
879        byte[] storeFirstKey = null;
880        byte[] storeLastKey = null;
881        for (FileStatus file : files) {
882          String fileName = file.getPath().toString();
883          fileName = fileName.substring(fileName.lastIndexOf("/") + 1);
884          if (!fileName.startsWith(".") && !fileName.endsWith("recovered.edits")) {
885            FileStatus[] storeFiles = fs.listStatus(file.getPath());
886            // For all the stores in this column family.
887            for (FileStatus storeFile : storeFiles) {
888              HFile.Reader reader =
889                HFile.createReader(fs, storeFile.getPath(), CacheConfig.DISABLED, true, getConf());
890              if (
891                (reader.getFirstKey() != null)
892                  && ((storeFirstKey == null) || (comparator.compare(storeFirstKey,
893                    ((KeyValue.KeyOnlyKeyValue) reader.getFirstKey().get()).getKey()) > 0))
894              ) {
895                storeFirstKey = ((KeyValue.KeyOnlyKeyValue) reader.getFirstKey().get()).getKey();
896              }
897              if (
898                (reader.getLastKey() != null)
899                  && ((storeLastKey == null) || (comparator.compare(storeLastKey,
900                    ((KeyValue.KeyOnlyKeyValue) reader.getLastKey().get()).getKey())) < 0)
901              ) {
902                storeLastKey = ((KeyValue.KeyOnlyKeyValue) reader.getLastKey().get()).getKey();
903              }
904              reader.close();
905            }
906          }
907        }
908        currentRegionBoundariesInformation.metaFirstKey = regionInfo.getStartKey();
909        currentRegionBoundariesInformation.metaLastKey = regionInfo.getEndKey();
910        currentRegionBoundariesInformation.storesFirstKey = keyOnly(storeFirstKey);
911        currentRegionBoundariesInformation.storesLastKey = keyOnly(storeLastKey);
912        if (currentRegionBoundariesInformation.metaFirstKey.length == 0)
913          currentRegionBoundariesInformation.metaFirstKey = null;
914        if (currentRegionBoundariesInformation.metaLastKey.length == 0)
915          currentRegionBoundariesInformation.metaLastKey = null;
916
917        // For a region to be correct, we need the META start key to be smaller or equal to the
918        // smallest start key from all the stores, and the start key from the next META entry to
919        // be bigger than the last key from all the current stores. First region start key is null;
920        // Last region end key is null; some regions can be empty and not have any store.
921
922        boolean valid = true;
923        // Checking start key.
924        if (
925          (currentRegionBoundariesInformation.storesFirstKey != null)
926            && (currentRegionBoundariesInformation.metaFirstKey != null)
927        ) {
928          valid = valid && comparator.compare(currentRegionBoundariesInformation.storesFirstKey,
929            currentRegionBoundariesInformation.metaFirstKey) >= 0;
930        }
931        // Checking stop key.
932        if (
933          (currentRegionBoundariesInformation.storesLastKey != null)
934            && (currentRegionBoundariesInformation.metaLastKey != null)
935        ) {
936          valid = valid && comparator.compare(currentRegionBoundariesInformation.storesLastKey,
937            currentRegionBoundariesInformation.metaLastKey) < 0;
938        }
939        if (!valid) {
940          errors.reportError(ERROR_CODE.BOUNDARIES_ERROR, "Found issues with regions boundaries",
941            tablesInfo.get(regionInfo.getTable()));
942          LOG.warn("Region's boundaries not aligned between stores and META for:");
943          LOG.warn(Objects.toString(currentRegionBoundariesInformation));
944        }
945      }
946    } catch (IOException e) {
947      LOG.error(e.toString(), e);
948    }
949  }
950
951  /**
952   * Iterates through the list of all orphan/invalid regiondirs.
953   */
954  private void adoptHdfsOrphans(Collection<HbckRegionInfo> orphanHdfsDirs) throws IOException {
955    for (HbckRegionInfo hi : orphanHdfsDirs) {
956      LOG.info("Attempting to handle orphan hdfs dir: " + hi.getHdfsRegionDir());
957      adoptHdfsOrphan(hi);
958    }
959  }
960
961  /**
962   * Orphaned regions are regions without a .regioninfo file in them. We "adopt" these orphans by
963   * creating a new region, and moving the column families, recovered edits, WALs, into the new
964   * region dir. We determine the region startkey and endkeys by looking at all of the hfiles inside
965   * the column families to identify the min and max keys. The resulting region will likely violate
966   * table integrity but will be dealt with by merging overlapping regions.
967   */
968  @SuppressWarnings("deprecation")
969  private void adoptHdfsOrphan(HbckRegionInfo hi) throws IOException {
970    Path p = hi.getHdfsRegionDir();
971    FileSystem fs = p.getFileSystem(getConf());
972    FileStatus[] dirs = fs.listStatus(p);
973    if (dirs == null) {
974      LOG.warn("Attempt to adopt orphan hdfs region skipped because no files present in " + p
975        + ". This dir could probably be deleted.");
976      return;
977    }
978
979    TableName tableName = hi.getTableName();
980    HbckTableInfo tableInfo = tablesInfo.get(tableName);
981    Preconditions.checkNotNull(tableInfo, "Table '" + tableName + "' not present!");
982    TableDescriptor template = tableInfo.getTableDescriptor();
983
984    // find min and max key values
985    Pair<byte[], byte[]> orphanRegionRange = null;
986    for (FileStatus cf : dirs) {
987      String cfName = cf.getPath().getName();
988      // TODO Figure out what the special dirs are
989      if (cfName.startsWith(".") || cfName.equals(HConstants.SPLIT_LOGDIR_NAME)) continue;
990
991      FileStatus[] hfiles = fs.listStatus(cf.getPath());
992      for (FileStatus hfile : hfiles) {
993        byte[] start, end;
994        HFile.Reader hf = null;
995        try {
996          hf = HFile.createReader(fs, hfile.getPath(), CacheConfig.DISABLED, true, getConf());
997          Optional<ExtendedCell> startKv = hf.getFirstKey();
998          start = CellUtil.cloneRow(startKv.get());
999          Optional<ExtendedCell> endKv = hf.getLastKey();
1000          end = CellUtil.cloneRow(endKv.get());
1001        } catch (Exception ioe) {
1002          LOG.warn("Problem reading orphan file " + hfile + ", skipping");
1003          continue;
1004        } finally {
1005          if (hf != null) {
1006            hf.close();
1007          }
1008        }
1009
1010        // expand the range to include the range of all hfiles
1011        if (orphanRegionRange == null) {
1012          // first range
1013          orphanRegionRange = new Pair<>(start, end);
1014        } else {
1015          // TODO add test
1016
1017          // expand range only if the hfile is wider.
1018          if (Bytes.compareTo(orphanRegionRange.getFirst(), start) > 0) {
1019            orphanRegionRange.setFirst(start);
1020          }
1021          if (Bytes.compareTo(orphanRegionRange.getSecond(), end) < 0) {
1022            orphanRegionRange.setSecond(end);
1023          }
1024        }
1025      }
1026    }
1027    if (orphanRegionRange == null) {
1028      LOG.warn("No data in dir " + p + ", sidelining data");
1029      fixes++;
1030      sidelineRegionDir(fs, hi);
1031      return;
1032    }
1033    LOG.info("Min max keys are : [" + Bytes.toString(orphanRegionRange.getFirst()) + ", "
1034      + Bytes.toString(orphanRegionRange.getSecond()) + ")");
1035
1036    // create new region on hdfs. move data into place.
1037    RegionInfo regionInfo = RegionInfoBuilder.newBuilder(template.getTableName())
1038      .setStartKey(orphanRegionRange.getFirst())
1039      .setEndKey(Bytes.add(orphanRegionRange.getSecond(), new byte[1])).build();
1040    LOG.info("Creating new region : " + regionInfo);
1041    HRegion region = HBaseFsckRepair.createHDFSRegionDir(getConf(), regionInfo, template);
1042    Path target = region.getRegionFileSystem().getRegionDir();
1043
1044    // rename all the data to new region
1045    mergeRegionDirs(target, hi);
1046    fixes++;
1047  }
1048
1049  /**
1050   * This method determines if there are table integrity errors in HDFS. If there are errors and the
1051   * appropriate "fix" options are enabled, the method will first correct orphan regions making them
1052   * into legit regiondirs, and then reload to merge potentially overlapping regions.
1053   * @return number of table integrity errors found
1054   */
1055  private int restoreHdfsIntegrity() throws IOException, InterruptedException {
1056    // Determine what's on HDFS
1057    LOG.info("Loading HBase regioninfo from HDFS...");
1058    loadHdfsRegionDirs(); // populating regioninfo table.
1059
1060    int errs = errors.getErrorList().size();
1061    // First time just get suggestions.
1062    tablesInfo = loadHdfsRegionInfos(); // update tableInfos based on region info in fs.
1063    checkHdfsIntegrity(false, false);
1064
1065    if (errors.getErrorList().size() == errs) {
1066      LOG.info("No integrity errors.  We are done with this phase. Glorious.");
1067      return 0;
1068    }
1069
1070    if (shouldFixHdfsOrphans() && orphanHdfsDirs.size() > 0) {
1071      adoptHdfsOrphans(orphanHdfsDirs);
1072      // TODO optimize by incrementally adding instead of reloading.
1073    }
1074
1075    // Make sure there are no holes now.
1076    if (shouldFixHdfsHoles()) {
1077      clearState(); // this also resets # fixes.
1078      loadHdfsRegionDirs();
1079      tablesInfo = loadHdfsRegionInfos(); // update tableInfos based on region info in fs.
1080      tablesInfo = checkHdfsIntegrity(shouldFixHdfsHoles(), false);
1081    }
1082
1083    // Now we fix overlaps
1084    if (shouldFixHdfsOverlaps()) {
1085      // second pass we fix overlaps.
1086      clearState(); // this also resets # fixes.
1087      loadHdfsRegionDirs();
1088      tablesInfo = loadHdfsRegionInfos(); // update tableInfos based on region info in fs.
1089      tablesInfo = checkHdfsIntegrity(false, shouldFixHdfsOverlaps());
1090    }
1091
1092    return errors.getErrorList().size();
1093  }
1094
1095  /**
1096   * Scan all the store file names to find any lingering reference files, which refer to some
1097   * none-exiting files. If "fix" option is enabled, any lingering reference file will be sidelined
1098   * if found.
1099   * <p>
1100   * Lingering reference file prevents a region from opening. It has to be fixed before a cluster
1101   * can start properly.
1102   */
1103  private void offlineReferenceFileRepair() throws IOException, InterruptedException {
1104    clearState();
1105    Configuration conf = getConf();
1106    Path hbaseRoot = CommonFSUtils.getRootDir(conf);
1107    FileSystem fs = hbaseRoot.getFileSystem(conf);
1108    LOG.info("Computing mapping of all store files");
1109    Map<String, Path> allFiles = FSUtils.getTableStoreFilePathMap(fs, hbaseRoot,
1110      new FSUtils.ReferenceFileFilter(fs), executor, errors);
1111    errors.print("");
1112    LOG.info("Validating mapping using HDFS state");
1113    for (Path path : allFiles.values()) {
1114      Path referredToFile = StoreFileInfo.getReferredToFile(path);
1115      if (fs.exists(referredToFile)) continue; // good, expected
1116
1117      // Found a lingering reference file
1118      errors.reportError(ERROR_CODE.LINGERING_REFERENCE_HFILE,
1119        "Found lingering reference file " + path);
1120      if (!shouldFixReferenceFiles()) continue;
1121
1122      // Now, trying to fix it since requested
1123      boolean success = false;
1124      String pathStr = path.toString();
1125
1126      // A reference file path should be like
1127      // ${hbase.rootdir}/data/namespace/table_name/region_id/family_name/referred_file.region_name
1128      // Up 5 directories to get the root folder.
1129      // So the file will be sidelined to a similar folder structure.
1130      int index = pathStr.lastIndexOf(Path.SEPARATOR_CHAR);
1131      for (int i = 0; index > 0 && i < 5; i++) {
1132        index = pathStr.lastIndexOf(Path.SEPARATOR_CHAR, index - 1);
1133      }
1134      if (index > 0) {
1135        Path rootDir = getSidelineDir();
1136        Path dst = new Path(rootDir, pathStr.substring(index + 1));
1137        fs.mkdirs(dst.getParent());
1138        LOG.info("Trying to sideline reference file " + path + " to " + dst);
1139        setShouldRerun();
1140
1141        success = fs.rename(path, dst);
1142        debugLsr(dst);
1143
1144      }
1145      if (!success) {
1146        LOG.error("Failed to sideline reference file " + path);
1147      }
1148    }
1149  }
1150
1151  /**
1152   * Scan all the store file names to find any lingering HFileLink files, which refer to some
1153   * none-exiting files. If "fix" option is enabled, any lingering HFileLink file will be sidelined
1154   * if found.
1155   */
1156  private void offlineHLinkFileRepair() throws IOException, InterruptedException {
1157    Configuration conf = getConf();
1158    Path hbaseRoot = CommonFSUtils.getRootDir(conf);
1159    FileSystem fs = hbaseRoot.getFileSystem(conf);
1160    LOG.info("Computing mapping of all link files");
1161    Map<String, Path> allFiles = FSUtils.getTableStoreFilePathMap(fs, hbaseRoot,
1162      new FSUtils.HFileLinkFilter(), executor, errors);
1163    errors.print("");
1164
1165    LOG.info("Validating mapping using HDFS state");
1166    for (Path path : allFiles.values()) {
1167      // building HFileLink object to gather locations
1168      HFileLink actualLink = HFileLink.buildFromHFileLinkPattern(conf, path);
1169      if (actualLink.exists(fs)) continue; // good, expected
1170
1171      // Found a lingering HFileLink
1172      errors.reportError(ERROR_CODE.LINGERING_HFILELINK, "Found lingering HFileLink " + path);
1173      if (!shouldFixHFileLinks()) continue;
1174
1175      // Now, trying to fix it since requested
1176      setShouldRerun();
1177
1178      // An HFileLink path should be like
1179      // ${hbase.rootdir}/data/namespace/table_name/region_id/family_name/linkedtable=linkedregionname-linkedhfilename
1180      // sidelineing will happen in the ${hbase.rootdir}/${sidelinedir} directory with the same
1181      // folder structure.
1182      boolean success = sidelineFile(fs, hbaseRoot, path);
1183
1184      if (!success) {
1185        LOG.error("Failed to sideline HFileLink file " + path);
1186      }
1187
1188      // An HFileLink backreference path should be like
1189      // ${hbase.rootdir}/archive/data/namespace/table_name/region_id/family_name/.links-linkedhfilename
1190      // sidelineing will happen in the ${hbase.rootdir}/${sidelinedir} directory with the same
1191      // folder structure.
1192      Path backRefPath = FileLink.getBackReferencesDir(
1193        HFileArchiveUtil.getStoreArchivePath(conf,
1194          HFileLink.getReferencedTableName(path.getName().toString()),
1195          HFileLink.getReferencedRegionName(path.getName().toString()), path.getParent().getName()),
1196        HFileLink.getReferencedHFileName(path.getName().toString()));
1197      success = sidelineFile(fs, hbaseRoot, backRefPath);
1198
1199      if (!success) {
1200        LOG.error("Failed to sideline HFileLink backreference file " + path);
1201      }
1202    }
1203  }
1204
1205  private boolean sidelineFile(FileSystem fs, Path hbaseRoot, Path path) throws IOException {
1206    URI uri = hbaseRoot.toUri().relativize(path.toUri());
1207    if (uri.isAbsolute()) return false;
1208    String relativePath = uri.getPath();
1209    Path rootDir = getSidelineDir();
1210    Path dst = new Path(rootDir, relativePath);
1211    boolean pathCreated = fs.mkdirs(dst.getParent());
1212    if (!pathCreated) {
1213      LOG.error("Failed to create path: " + dst.getParent());
1214      return false;
1215    }
1216    LOG.info("Trying to sideline file " + path + " to " + dst);
1217    return fs.rename(path, dst);
1218  }
1219
1220  /**
1221   * TODO -- need to add tests for this.
1222   */
1223  private void reportEmptyMetaCells() {
1224    errors.print("Number of empty REGIONINFO_QUALIFIER rows in " + TableName.META_TABLE_NAME + ": "
1225      + emptyRegionInfoQualifiers.size());
1226    if (details) {
1227      for (Result r : emptyRegionInfoQualifiers) {
1228        errors.print("  " + r);
1229      }
1230    }
1231  }
1232
1233  /**
1234   * TODO -- need to add tests for this.
1235   */
1236  private void reportTablesInFlux() {
1237    AtomicInteger numSkipped = new AtomicInteger(0);
1238    TableDescriptor[] allTables = getTables(numSkipped);
1239    errors.print("Number of Tables: " + allTables.length);
1240    if (details) {
1241      if (numSkipped.get() > 0) {
1242        errors.detail("Number of Tables in flux: " + numSkipped.get());
1243      }
1244      for (TableDescriptor td : allTables) {
1245        errors.detail("  Table: " + td.getTableName() + "\t" + (td.isReadOnly() ? "ro" : "rw")
1246          + "\t" + (td.isMetaRegion() ? "META" : "    ") + "\t" + " families: "
1247          + td.getColumnFamilyCount());
1248      }
1249    }
1250  }
1251
1252  public HbckErrorReporter getErrors() {
1253    return errors;
1254  }
1255
1256  /**
1257   * Populate hbi's from regionInfos loaded from file system.
1258   */
1259  private SortedMap<TableName, HbckTableInfo> loadHdfsRegionInfos()
1260    throws IOException, InterruptedException {
1261    tablesInfo.clear(); // regenerating the data
1262    // generate region split structure
1263    Collection<HbckRegionInfo> hbckRegionInfos = regionInfoMap.values();
1264
1265    // Parallelized read of .regioninfo files.
1266    List<WorkItemHdfsRegionInfo> hbis = new ArrayList<>(hbckRegionInfos.size());
1267    List<Future<Void>> hbiFutures;
1268
1269    for (HbckRegionInfo hbi : hbckRegionInfos) {
1270      WorkItemHdfsRegionInfo work = new WorkItemHdfsRegionInfo(hbi, this, errors);
1271      hbis.add(work);
1272    }
1273
1274    // Submit and wait for completion
1275    hbiFutures = executor.invokeAll(hbis);
1276
1277    for (int i = 0; i < hbiFutures.size(); i++) {
1278      WorkItemHdfsRegionInfo work = hbis.get(i);
1279      Future<Void> f = hbiFutures.get(i);
1280      try {
1281        f.get();
1282      } catch (ExecutionException e) {
1283        LOG.warn("Failed to read .regioninfo file for region " + work.hbi.getRegionNameAsString(),
1284          e.getCause());
1285      }
1286    }
1287
1288    Path hbaseRoot = CommonFSUtils.getRootDir(getConf());
1289    FileSystem fs = hbaseRoot.getFileSystem(getConf());
1290    // serialized table info gathering.
1291    for (HbckRegionInfo hbi : hbckRegionInfos) {
1292
1293      if (hbi.getHdfsHRI() == null) {
1294        // was an orphan
1295        continue;
1296      }
1297
1298      // get table name from hdfs, populate various HBaseFsck tables.
1299      TableName tableName = hbi.getTableName();
1300      if (tableName == null) {
1301        // There was an entry in hbase:meta not in the HDFS?
1302        LOG.warn("tableName was null for: " + hbi);
1303        continue;
1304      }
1305
1306      HbckTableInfo modTInfo = tablesInfo.get(tableName);
1307      if (modTInfo == null) {
1308        // only executed once per table.
1309        modTInfo = new HbckTableInfo(tableName, this);
1310        tablesInfo.put(tableName, modTInfo);
1311        try {
1312          TableDescriptor htd =
1313            FSTableDescriptors.getTableDescriptorFromFs(fs, hbaseRoot, tableName);
1314          modTInfo.htds.add(htd);
1315        } catch (IOException ioe) {
1316          if (!orphanTableDirs.containsKey(tableName)) {
1317            LOG.warn("Unable to read .tableinfo from " + hbaseRoot, ioe);
1318            // should only report once for each table
1319            errors.reportError(ERROR_CODE.NO_TABLEINFO_FILE,
1320              "Unable to read .tableinfo from " + hbaseRoot + "/" + tableName);
1321            Set<String> columns = new HashSet<>();
1322            orphanTableDirs.put(tableName, getColumnFamilyList(columns, hbi));
1323          }
1324        }
1325      }
1326      if (!hbi.isSkipChecks()) {
1327        modTInfo.addRegionInfo(hbi);
1328      }
1329    }
1330
1331    loadTableInfosForTablesWithNoRegion();
1332    errors.print("");
1333
1334    return tablesInfo;
1335  }
1336
1337  /**
1338   * To get the column family list according to the column family dirs
1339   * @return a set of column families
1340   */
1341  private Set<String> getColumnFamilyList(Set<String> columns, HbckRegionInfo hbi)
1342    throws IOException {
1343    Path regionDir = hbi.getHdfsRegionDir();
1344    FileSystem fs = regionDir.getFileSystem(getConf());
1345    FileStatus[] subDirs = fs.listStatus(regionDir, new FSUtils.FamilyDirFilter(fs));
1346    for (FileStatus subdir : subDirs) {
1347      String columnfamily = subdir.getPath().getName();
1348      columns.add(columnfamily);
1349    }
1350    return columns;
1351  }
1352
1353  /**
1354   * To fabricate a .tableinfo file with following contents<br>
1355   * 1. the correct tablename <br>
1356   * 2. the correct colfamily list<br>
1357   * 3. the default properties for both {@link TableDescriptor} and
1358   * {@link ColumnFamilyDescriptor}<br>
1359   */
1360  private boolean fabricateTableInfo(FSTableDescriptors fstd, TableName tableName,
1361    Set<String> columns) throws IOException {
1362    if (columns == null || columns.isEmpty()) return false;
1363    TableDescriptorBuilder builder = TableDescriptorBuilder.newBuilder(tableName);
1364    for (String columnfamimly : columns) {
1365      builder.setColumnFamily(ColumnFamilyDescriptorBuilder.of(columnfamimly));
1366    }
1367    fstd.createTableDescriptor(builder.build(), true);
1368    return true;
1369  }
1370
1371  /**
1372   * To fix the empty REGIONINFO_QUALIFIER rows from hbase:meta <br>
1373   */
1374  public void fixEmptyMetaCells() throws IOException {
1375    if (shouldFixEmptyMetaCells() && !emptyRegionInfoQualifiers.isEmpty()) {
1376      LOG.info("Trying to fix empty REGIONINFO_QUALIFIER {} rows.", TableName.META_TABLE_NAME);
1377      for (Result region : emptyRegionInfoQualifiers) {
1378        deleteMetaRegion(region.getRow());
1379        errors.getErrorList().remove(ERROR_CODE.EMPTY_META_CELL);
1380      }
1381      emptyRegionInfoQualifiers.clear();
1382    }
1383  }
1384
1385  /**
1386   * To fix orphan table by creating a .tableinfo file under tableDir <br>
1387   * 1. if TableInfo is cached, to recover the .tableinfo accordingly <br>
1388   * 2. else create a default .tableinfo file with following items<br>
1389   * &nbsp;2.1 the correct tablename <br>
1390   * &nbsp;2.2 the correct colfamily list<br>
1391   * &nbsp;2.3 the default properties for both {@link TableDescriptor} and
1392   * {@link ColumnFamilyDescriptor}<br>
1393   */
1394  public void fixOrphanTables() throws IOException {
1395    if (shouldFixTableOrphans() && !orphanTableDirs.isEmpty()) {
1396
1397      List<TableName> tmpList = new ArrayList<>(orphanTableDirs.keySet().size());
1398      tmpList.addAll(orphanTableDirs.keySet());
1399      TableDescriptor[] htds = getTableDescriptors(tmpList);
1400      Iterator<Entry<TableName, Set<String>>> iter = orphanTableDirs.entrySet().iterator();
1401      int j = 0;
1402      int numFailedCase = 0;
1403      FSTableDescriptors fstd = new FSTableDescriptors(getConf());
1404      while (iter.hasNext()) {
1405        Entry<TableName, Set<String>> entry = iter.next();
1406        TableName tableName = entry.getKey();
1407        LOG.info("Trying to fix orphan table error: " + tableName);
1408        if (j < htds.length) {
1409          if (tableName.equals(htds[j].getTableName())) {
1410            TableDescriptor htd = htds[j];
1411            LOG.info("fixing orphan table: " + tableName + " from cache");
1412            fstd.createTableDescriptor(htd, true);
1413            j++;
1414            iter.remove();
1415          }
1416        } else {
1417          if (fabricateTableInfo(fstd, tableName, entry.getValue())) {
1418            LOG.warn("fixing orphan table: " + tableName + " with a default .tableinfo file");
1419            LOG.warn(
1420              "Strongly recommend to modify the TableDescriptor if necessary for: " + tableName);
1421            iter.remove();
1422          } else {
1423            LOG.error("Unable to create default .tableinfo for " + tableName
1424              + " while missing column family information");
1425            numFailedCase++;
1426          }
1427        }
1428        fixes++;
1429      }
1430
1431      if (orphanTableDirs.isEmpty()) {
1432        // all orphanTableDirs are luckily recovered
1433        // re-run doFsck after recovering the .tableinfo file
1434        setShouldRerun();
1435        LOG.warn(
1436          "Strongly recommend to re-run manually hfsck after all orphanTableDirs being fixed");
1437      } else if (numFailedCase > 0) {
1438        LOG.error("Failed to fix " + numFailedCase + " OrphanTables with default .tableinfo files");
1439      }
1440
1441    }
1442    // cleanup the list
1443    orphanTableDirs.clear();
1444
1445  }
1446
1447  /**
1448   * Log an appropriate message about whether or not overlapping merges are computed in parallel.
1449   */
1450  private void logParallelMerge() {
1451    if (getConf().getBoolean("hbasefsck.overlap.merge.parallel", true)) {
1452      LOG.info("Handling overlap merges in parallel. set hbasefsck.overlap.merge.parallel to"
1453        + " false to run serially.");
1454    } else {
1455      LOG.info("Handling overlap merges serially.  set hbasefsck.overlap.merge.parallel to"
1456        + " true to run in parallel.");
1457    }
1458  }
1459
1460  private SortedMap<TableName, HbckTableInfo> checkHdfsIntegrity(boolean fixHoles,
1461    boolean fixOverlaps) throws IOException {
1462    LOG.info("Checking HBase region split map from HDFS data...");
1463    logParallelMerge();
1464    for (HbckTableInfo tInfo : tablesInfo.values()) {
1465      TableIntegrityErrorHandler handler;
1466      if (fixHoles || fixOverlaps) {
1467        handler = tInfo.new HDFSIntegrityFixer(tInfo, errors, getConf(), fixHoles, fixOverlaps);
1468      } else {
1469        handler = tInfo.new IntegrityFixSuggester(tInfo, errors);
1470      }
1471      if (!tInfo.checkRegionChain(handler)) {
1472        // should dump info as well.
1473        errors.report("Found inconsistency in table " + tInfo.getName());
1474      }
1475    }
1476    return tablesInfo;
1477  }
1478
1479  Path getSidelineDir() throws IOException {
1480    if (sidelineDir == null) {
1481      Path hbaseDir = CommonFSUtils.getRootDir(getConf());
1482      Path hbckDir = new Path(hbaseDir, HConstants.HBCK_SIDELINEDIR_NAME);
1483      sidelineDir = new Path(hbckDir, hbaseDir.getName() + "-" + startMillis);
1484    }
1485    return sidelineDir;
1486  }
1487
1488  /**
1489   * Sideline a region dir (instead of deleting it)
1490   */
1491  Path sidelineRegionDir(FileSystem fs, HbckRegionInfo hi) throws IOException {
1492    return sidelineRegionDir(fs, null, hi);
1493  }
1494
1495  /**
1496   * Sideline a region dir (instead of deleting it)
1497   * @param parentDir if specified, the region will be sidelined to folder like
1498   *                  {@literal .../parentDir/<table name>/<region name>}. The purpose is to group
1499   *                  together similar regions sidelined, for example, those regions should be bulk
1500   *                  loaded back later on. If NULL, it is ignored.
1501   */
1502  Path sidelineRegionDir(FileSystem fs, String parentDir, HbckRegionInfo hi) throws IOException {
1503    TableName tableName = hi.getTableName();
1504    Path regionDir = hi.getHdfsRegionDir();
1505
1506    if (!fs.exists(regionDir)) {
1507      LOG.warn("No previous " + regionDir + " exists.  Continuing.");
1508      return null;
1509    }
1510
1511    Path rootDir = getSidelineDir();
1512    if (parentDir != null) {
1513      rootDir = new Path(rootDir, parentDir);
1514    }
1515    Path sidelineTableDir = CommonFSUtils.getTableDir(rootDir, tableName);
1516    Path sidelineRegionDir = new Path(sidelineTableDir, regionDir.getName());
1517    fs.mkdirs(sidelineRegionDir);
1518    boolean success = false;
1519    FileStatus[] cfs = fs.listStatus(regionDir);
1520    if (cfs == null) {
1521      LOG.info("Region dir is empty: " + regionDir);
1522    } else {
1523      for (FileStatus cf : cfs) {
1524        Path src = cf.getPath();
1525        Path dst = new Path(sidelineRegionDir, src.getName());
1526        if (fs.isFile(src)) {
1527          // simple file
1528          success = fs.rename(src, dst);
1529          if (!success) {
1530            String msg = "Unable to rename file " + src + " to " + dst;
1531            LOG.error(msg);
1532            throw new IOException(msg);
1533          }
1534          continue;
1535        }
1536
1537        // is a directory.
1538        fs.mkdirs(dst);
1539
1540        LOG.info("Sidelining files from " + src + " into containing region " + dst);
1541        // FileSystem.rename is inconsistent with directories -- if the
1542        // dst (foo/a) exists and is a dir, and the src (foo/b) is a dir,
1543        // it moves the src into the dst dir resulting in (foo/a/b). If
1544        // the dst does not exist, and the src a dir, src becomes dst. (foo/b)
1545        FileStatus[] hfiles = fs.listStatus(src);
1546        if (hfiles != null && hfiles.length > 0) {
1547          for (FileStatus hfile : hfiles) {
1548            success = fs.rename(hfile.getPath(), dst);
1549            if (!success) {
1550              String msg = "Unable to rename file " + src + " to " + dst;
1551              LOG.error(msg);
1552              throw new IOException(msg);
1553            }
1554          }
1555        }
1556        LOG.debug("Sideline directory contents:");
1557        debugLsr(sidelineRegionDir);
1558      }
1559    }
1560
1561    LOG.info("Removing old region dir: " + regionDir);
1562    success = fs.delete(regionDir, true);
1563    if (!success) {
1564      String msg = "Unable to delete dir " + regionDir;
1565      LOG.error(msg);
1566      throw new IOException(msg);
1567    }
1568    return sidelineRegionDir;
1569  }
1570
1571  /**
1572   * Load the list of disabled tables in ZK into local set.
1573   */
1574  private void loadTableStates() throws IOException {
1575    tableStates = MetaTableAccessor.getTableStates(connection);
1576    // Add hbase:meta so this tool keeps working. In hbase2, meta is always enabled though it
1577    // has no entry in the table states. HBCK doesn't work right w/ hbase2 but just do this in
1578    // meantime.
1579    this.tableStates.put(TableName.META_TABLE_NAME,
1580      new TableState(TableName.META_TABLE_NAME, TableState.State.ENABLED));
1581  }
1582
1583  /**
1584   * Check if the specified region's table is disabled.
1585   * @param tableName table to check status of
1586   */
1587  boolean isTableDisabled(TableName tableName) {
1588    return tableStates.containsKey(tableName)
1589      && tableStates.get(tableName).inStates(TableState.State.DISABLED, TableState.State.DISABLING);
1590  }
1591
1592  /**
1593   * Scan HDFS for all regions, recording their information into regionInfoMap
1594   */
1595  public void loadHdfsRegionDirs() throws IOException, InterruptedException {
1596    Path rootDir = CommonFSUtils.getRootDir(getConf());
1597    FileSystem fs = rootDir.getFileSystem(getConf());
1598
1599    // list all tables from HDFS
1600    List<FileStatus> tableDirs = Lists.newArrayList();
1601
1602    boolean foundVersionFile = fs.exists(new Path(rootDir, HConstants.VERSION_FILE_NAME));
1603
1604    List<Path> paths = FSUtils.getTableDirs(fs, rootDir);
1605    for (Path path : paths) {
1606      TableName tableName = CommonFSUtils.getTableName(path);
1607      if (
1608        (!checkMetaOnly && isTableIncluded(tableName))
1609          || tableName.equals(TableName.META_TABLE_NAME)
1610      ) {
1611        tableDirs.add(fs.getFileStatus(path));
1612      }
1613    }
1614
1615    // verify that version file exists
1616    if (!foundVersionFile) {
1617      errors.reportError(ERROR_CODE.NO_VERSION_FILE,
1618        "Version file does not exist in root dir " + rootDir);
1619      if (shouldFixVersionFile()) {
1620        LOG.info("Trying to create a new " + HConstants.VERSION_FILE_NAME + " file.");
1621        setShouldRerun();
1622        FSUtils.setVersion(fs, rootDir,
1623          getConf().getInt(HConstants.THREAD_WAKE_FREQUENCY, 10 * 1000),
1624          getConf().getInt(HConstants.VERSION_FILE_WRITE_ATTEMPTS,
1625            HConstants.DEFAULT_VERSION_FILE_WRITE_ATTEMPTS));
1626      }
1627    }
1628
1629    // Avoid multithreading at table-level because already multithreaded internally at
1630    // region-level. Additionally multithreading at table-level can lead to deadlock
1631    // if there are many tables in the cluster. Since there are a limited # of threads
1632    // in the executor's thread pool and if we multithread at the table-level by putting
1633    // WorkItemHdfsDir callables into the executor, then we will have some threads in the
1634    // executor tied up solely in waiting for the tables' region-level calls to complete.
1635    // If there are enough tables then there will be no actual threads in the pool left
1636    // for the region-level callables to be serviced.
1637    for (FileStatus tableDir : tableDirs) {
1638      LOG.debug("Loading region dirs from " + tableDir.getPath());
1639      WorkItemHdfsDir item = new WorkItemHdfsDir(fs, errors, tableDir);
1640      try {
1641        item.call();
1642      } catch (ExecutionException e) {
1643        LOG.warn("Could not completely load table dir " + tableDir.getPath(), e.getCause());
1644      }
1645    }
1646    errors.print("");
1647  }
1648
1649  /**
1650   * Record the location of the hbase:meta region as found in ZooKeeper.
1651   */
1652  private boolean recordMetaRegion() throws IOException {
1653    List<HRegionLocation> locs;
1654    try (RegionLocator locator = connection.getRegionLocator(TableName.META_TABLE_NAME)) {
1655      locs = locator.getRegionLocations(HConstants.EMPTY_START_ROW, true);
1656    }
1657    if (locs == null || locs.isEmpty()) {
1658      errors.reportError(ERROR_CODE.NULL_META_REGION, "META region was not found in ZooKeeper");
1659      return false;
1660    }
1661    for (HRegionLocation metaLocation : locs) {
1662      // Check if Meta region is valid and existing
1663      if (metaLocation == null) {
1664        errors.reportError(ERROR_CODE.NULL_META_REGION, "META region location is null");
1665        return false;
1666      }
1667      if (metaLocation.getRegion() == null) {
1668        errors.reportError(ERROR_CODE.NULL_META_REGION, "META location regionInfo is null");
1669        return false;
1670      }
1671      if (metaLocation.getHostname() == null) {
1672        errors.reportError(ERROR_CODE.NULL_META_REGION, "META location hostName is null");
1673        return false;
1674      }
1675      ServerName sn = metaLocation.getServerName();
1676      HbckRegionInfo.MetaEntry m = new HbckRegionInfo.MetaEntry(metaLocation.getRegion(), sn,
1677        EnvironmentEdgeManager.currentTime());
1678      HbckRegionInfo hbckRegionInfo = regionInfoMap.get(metaLocation.getRegion().getEncodedName());
1679      if (hbckRegionInfo == null) {
1680        regionInfoMap.put(metaLocation.getRegion().getEncodedName(), new HbckRegionInfo(m));
1681      } else {
1682        hbckRegionInfo.setMetaEntry(m);
1683      }
1684    }
1685    return true;
1686  }
1687
1688  private ZKWatcher createZooKeeperWatcher() throws IOException {
1689    return new ZKWatcher(getConf(), "hbase Fsck", new Abortable() {
1690      @Override
1691      public void abort(String why, Throwable e) {
1692        LOG.error(why, e);
1693        System.exit(1);
1694      }
1695
1696      @Override
1697      public boolean isAborted() {
1698        return false;
1699      }
1700
1701    });
1702  }
1703
1704  /**
1705   * Contacts each regionserver and fetches metadata about regions.
1706   * @param regionServerList - the list of region servers to connect to
1707   * @throws IOException if a remote or network exception occurs
1708   */
1709  void processRegionServers(Collection<ServerName> regionServerList)
1710    throws IOException, InterruptedException {
1711
1712    List<WorkItemRegion> workItems = new ArrayList<>(regionServerList.size());
1713    List<Future<Void>> workFutures;
1714
1715    // loop to contact each region server in parallel
1716    for (ServerName rsinfo : regionServerList) {
1717      workItems.add(new WorkItemRegion(this, rsinfo, errors, connection));
1718    }
1719
1720    workFutures = executor.invokeAll(workItems);
1721
1722    for (int i = 0; i < workFutures.size(); i++) {
1723      WorkItemRegion item = workItems.get(i);
1724      Future<Void> f = workFutures.get(i);
1725      try {
1726        f.get();
1727      } catch (ExecutionException e) {
1728        LOG.warn("Could not process regionserver {}", item.rsinfo.getAddress(), e.getCause());
1729      }
1730    }
1731  }
1732
1733  /**
1734   * Check consistency of all regions that have been found in previous phases.
1735   */
1736  private void checkAndFixConsistency() throws IOException, KeeperException, InterruptedException {
1737    // Divide the checks in two phases. One for default/primary replicas and another
1738    // for the non-primary ones. Keeps code cleaner this way.
1739
1740    List<CheckRegionConsistencyWorkItem> workItems = new ArrayList<>(regionInfoMap.size());
1741    for (java.util.Map.Entry<String, HbckRegionInfo> e : regionInfoMap.entrySet()) {
1742      if (e.getValue().getReplicaId() == RegionInfo.DEFAULT_REPLICA_ID) {
1743        workItems.add(new CheckRegionConsistencyWorkItem(e.getKey(), e.getValue()));
1744      }
1745    }
1746    checkRegionConsistencyConcurrently(workItems);
1747
1748    boolean prevHdfsCheck = shouldCheckHdfs();
1749    setCheckHdfs(false); // replicas don't have any hdfs data
1750    // Run a pass over the replicas and fix any assignment issues that exist on the currently
1751    // deployed/undeployed replicas.
1752    List<CheckRegionConsistencyWorkItem> replicaWorkItems = new ArrayList<>(regionInfoMap.size());
1753    for (java.util.Map.Entry<String, HbckRegionInfo> e : regionInfoMap.entrySet()) {
1754      if (e.getValue().getReplicaId() != RegionInfo.DEFAULT_REPLICA_ID) {
1755        replicaWorkItems.add(new CheckRegionConsistencyWorkItem(e.getKey(), e.getValue()));
1756      }
1757    }
1758    checkRegionConsistencyConcurrently(replicaWorkItems);
1759    setCheckHdfs(prevHdfsCheck);
1760
1761    // If some regions is skipped during checkRegionConsistencyConcurrently() phase, we might
1762    // not get accurate state of the hbase if continuing. The config here allows users to tune
1763    // the tolerance of number of skipped region.
1764    // TODO: evaluate the consequence to continue the hbck operation without config.
1765    int terminateThreshold = getConf().getInt("hbase.hbck.skipped.regions.limit", 0);
1766    int numOfSkippedRegions = skippedRegions.size();
1767    if (numOfSkippedRegions > 0 && numOfSkippedRegions > terminateThreshold) {
1768      throw new IOException(
1769        numOfSkippedRegions + " region(s) could not be checked or repaired.  See logs for detail.");
1770    }
1771
1772    if (shouldCheckHdfs()) {
1773      checkAndFixTableStates();
1774    }
1775  }
1776
1777  /**
1778   * Check consistency of all regions using multiple threads concurrently.
1779   */
1780  private void
1781    checkRegionConsistencyConcurrently(final List<CheckRegionConsistencyWorkItem> workItems)
1782      throws IOException, KeeperException, InterruptedException {
1783    if (workItems.isEmpty()) {
1784      return; // nothing to check
1785    }
1786
1787    List<Future<Void>> workFutures = executor.invokeAll(workItems);
1788    for (Future<Void> f : workFutures) {
1789      try {
1790        f.get();
1791      } catch (ExecutionException e1) {
1792        LOG.warn("Could not check region consistency ", e1.getCause());
1793        if (e1.getCause() instanceof IOException) {
1794          throw (IOException) e1.getCause();
1795        } else if (e1.getCause() instanceof KeeperException) {
1796          throw (KeeperException) e1.getCause();
1797        } else if (e1.getCause() instanceof InterruptedException) {
1798          throw (InterruptedException) e1.getCause();
1799        } else {
1800          throw new IOException(e1.getCause());
1801        }
1802      }
1803    }
1804  }
1805
1806  class CheckRegionConsistencyWorkItem implements Callable<Void> {
1807    private final String key;
1808    private final HbckRegionInfo hbi;
1809
1810    CheckRegionConsistencyWorkItem(String key, HbckRegionInfo hbi) {
1811      this.key = key;
1812      this.hbi = hbi;
1813    }
1814
1815    @Override
1816    public synchronized Void call() throws Exception {
1817      try {
1818        checkRegionConsistency(key, hbi);
1819      } catch (Exception e) {
1820        // If the region is non-META region, skip this region and send warning/error message; if
1821        // the region is META region, we should not continue.
1822        LOG.warn(
1823          "Unable to complete check or repair the region '" + hbi.getRegionNameAsString() + "'.",
1824          e);
1825        if (hbi.getHdfsHRI().isMetaRegion()) {
1826          throw e;
1827        }
1828        LOG.warn("Skip region '" + hbi.getRegionNameAsString() + "'");
1829        addSkippedRegion(hbi);
1830      }
1831      return null;
1832    }
1833  }
1834
1835  private void addSkippedRegion(final HbckRegionInfo hbi) {
1836    Set<String> skippedRegionNames = skippedRegions.get(hbi.getTableName());
1837    if (skippedRegionNames == null) {
1838      skippedRegionNames = new HashSet<>();
1839    }
1840    skippedRegionNames.add(hbi.getRegionNameAsString());
1841    skippedRegions.put(hbi.getTableName(), skippedRegionNames);
1842  }
1843
1844  /**
1845   * Check and fix table states, assumes full info available: - tableInfos - empty tables loaded
1846   */
1847  private void checkAndFixTableStates() throws IOException {
1848    // first check dangling states
1849    for (Entry<TableName, TableState> entry : tableStates.entrySet()) {
1850      TableName tableName = entry.getKey();
1851      TableState tableState = entry.getValue();
1852      HbckTableInfo tableInfo = tablesInfo.get(tableName);
1853      if (isTableIncluded(tableName) && !tableName.isSystemTable() && tableInfo == null) {
1854        if (fixMeta) {
1855          MetaTableAccessor.deleteTableState(connection, tableName);
1856          TableState state = MetaTableAccessor.getTableState(connection, tableName);
1857          if (state != null) {
1858            errors.reportError(ERROR_CODE.ORPHAN_TABLE_STATE,
1859              tableName + " unable to delete dangling table state " + tableState);
1860          }
1861        } else if (!checkMetaOnly) {
1862          // dangling table state in meta if checkMetaOnly is false. If checkMetaOnly is
1863          // true, tableInfo will be null as tablesInfo are not polulated for all tables from hdfs
1864          errors.reportError(ERROR_CODE.ORPHAN_TABLE_STATE,
1865            tableName + " has dangling table state " + tableState);
1866        }
1867      }
1868    }
1869    // check that all tables have states
1870    for (TableName tableName : tablesInfo.keySet()) {
1871      if (isTableIncluded(tableName) && !tableStates.containsKey(tableName)) {
1872        if (fixMeta) {
1873          MetaTableAccessor.updateTableState(connection, tableName, TableState.State.ENABLED);
1874          TableState newState = MetaTableAccessor.getTableState(connection, tableName);
1875          if (newState == null) {
1876            errors.reportError(ERROR_CODE.NO_TABLE_STATE,
1877              "Unable to change state for table " + tableName + " in meta ");
1878          }
1879        } else {
1880          errors.reportError(ERROR_CODE.NO_TABLE_STATE, tableName + " has no state in meta ");
1881        }
1882      }
1883    }
1884  }
1885
1886  private void preCheckPermission() throws IOException {
1887    if (shouldIgnorePreCheckPermission()) {
1888      return;
1889    }
1890
1891    Path hbaseDir = CommonFSUtils.getRootDir(getConf());
1892    FileSystem fs = hbaseDir.getFileSystem(getConf());
1893    UserProvider userProvider = UserProvider.instantiate(getConf());
1894    UserGroupInformation ugi = userProvider.getCurrent().getUGI();
1895    FileStatus[] files = fs.listStatus(hbaseDir);
1896    for (FileStatus file : files) {
1897      try {
1898        fs.access(file.getPath(), FsAction.WRITE);
1899      } catch (AccessControlException ace) {
1900        LOG.warn("Got AccessDeniedException when preCheckPermission ", ace);
1901        errors.reportError(ERROR_CODE.WRONG_USAGE,
1902          "Current user " + ugi.getUserName() + " does not have write perms to " + file.getPath()
1903            + ". Please rerun hbck as hdfs user " + file.getOwner());
1904        throw ace;
1905      }
1906    }
1907  }
1908
1909  /**
1910   * Deletes region from meta table
1911   */
1912  private void deleteMetaRegion(HbckRegionInfo hi) throws IOException {
1913    deleteMetaRegion(hi.getMetaEntry().getRegionInfo().getRegionName());
1914  }
1915
1916  /**
1917   * Deletes region from meta table
1918   */
1919  private void deleteMetaRegion(byte[] metaKey) throws IOException {
1920    Delete d = new Delete(metaKey);
1921    meta.delete(d);
1922    LOG.info("Deleted " + Bytes.toString(metaKey) + " from META");
1923  }
1924
1925  /**
1926   * Reset the split parent region info in meta table
1927   */
1928  private void resetSplitParent(HbckRegionInfo hi) throws IOException {
1929    RowMutations mutations = new RowMutations(hi.getMetaEntry().getRegionInfo().getRegionName());
1930    Delete d = new Delete(hi.getMetaEntry().getRegionInfo().getRegionName());
1931    d.addColumn(HConstants.CATALOG_FAMILY, HConstants.SPLITA_QUALIFIER);
1932    d.addColumn(HConstants.CATALOG_FAMILY, HConstants.SPLITB_QUALIFIER);
1933    mutations.add(d);
1934
1935    RegionInfo hri = RegionInfoBuilder.newBuilder(hi.getMetaEntry().getRegionInfo())
1936      .setOffline(false).setSplit(false).build();
1937    Put p = MetaTableAccessor.makePutFromRegionInfo(hri);
1938    mutations.add(p);
1939
1940    meta.mutateRow(mutations);
1941    LOG.info("Reset split parent " + hi.getMetaEntry().getRegionInfo().getRegionNameAsString()
1942      + " in META");
1943  }
1944
1945  /**
1946   * This backwards-compatibility wrapper for permanently offlining a region that should not be
1947   * alive. If the region server does not support the "offline" method, it will use the closest
1948   * unassign method instead. This will basically work until one attempts to disable or delete the
1949   * affected table. The problem has to do with in-memory only master state, so restarting the
1950   * HMaster or failing over to another should fix this.
1951   */
1952  void offline(byte[] regionName) throws IOException {
1953    String regionString = Bytes.toStringBinary(regionName);
1954    if (!rsSupportsOffline) {
1955      LOG.warn("Using unassign region " + regionString
1956        + " instead of using offline method, you should" + " restart HMaster after these repairs");
1957      admin.unassign(regionName, true);
1958      return;
1959    }
1960
1961    // first time we assume the rs's supports #offline.
1962    try {
1963      LOG.info("Offlining region " + regionString);
1964      admin.offline(regionName);
1965    } catch (IOException ioe) {
1966      String notFoundMsg =
1967        "java.lang.NoSuchMethodException: " + "org.apache.hadoop.hbase.master.HMaster.offline([B)";
1968      if (ioe.getMessage().contains(notFoundMsg)) {
1969        LOG.warn(
1970          "Using unassign region " + regionString + " instead of using offline method, you should"
1971            + " restart HMaster after these repairs");
1972        rsSupportsOffline = false; // in the future just use unassign
1973        admin.unassign(regionName, true);
1974        return;
1975      }
1976      throw ioe;
1977    }
1978  }
1979
1980  /**
1981   * Attempts to undeploy a region from a region server based in information in META. Any operations
1982   * that modify the file system should make sure that its corresponding region is not deployed to
1983   * prevent data races. A separate call is required to update the master in-memory region state
1984   * kept in the AssignementManager. Because disable uses this state instead of that found in META,
1985   * we can't seem to cleanly disable/delete tables that have been hbck fixed. When used on a
1986   * version of HBase that does not have the offline ipc call exposed on the master (&lt;0.90.5,
1987   * &lt;0.92.0) a master restart or failover may be required.
1988   */
1989  void closeRegion(HbckRegionInfo hi) throws IOException, InterruptedException {
1990    if (hi.getMetaEntry() == null && hi.getHdfsEntry() == null) {
1991      undeployRegions(hi);
1992      return;
1993    }
1994
1995    // get assignment info and hregioninfo from meta.
1996    Get get = new Get(hi.getRegionName());
1997    get.addColumn(HConstants.CATALOG_FAMILY, HConstants.REGIONINFO_QUALIFIER);
1998    get.addColumn(HConstants.CATALOG_FAMILY, HConstants.SERVER_QUALIFIER);
1999    get.addColumn(HConstants.CATALOG_FAMILY, HConstants.STARTCODE_QUALIFIER);
2000    // also get the locations of the replicas to close if the primary region is being closed
2001    if (hi.getReplicaId() == RegionInfo.DEFAULT_REPLICA_ID) {
2002      int numReplicas = admin.getDescriptor(hi.getTableName()).getRegionReplication();
2003      for (int i = 0; i < numReplicas; i++) {
2004        get.addColumn(HConstants.CATALOG_FAMILY, CatalogFamilyFormat.getServerColumn(i));
2005        get.addColumn(HConstants.CATALOG_FAMILY, CatalogFamilyFormat.getStartCodeColumn(i));
2006      }
2007    }
2008    Result r = meta.get(get);
2009    RegionLocations rl = CatalogFamilyFormat.getRegionLocations(r);
2010    if (rl == null) {
2011      LOG.warn("Unable to close region " + hi.getRegionNameAsString()
2012        + " since meta does not have handle to reach it");
2013      return;
2014    }
2015    for (HRegionLocation h : rl.getRegionLocations()) {
2016      ServerName serverName = h.getServerName();
2017      if (serverName == null) {
2018        errors.reportError("Unable to close region " + hi.getRegionNameAsString()
2019          + " because meta does not " + "have handle to reach it.");
2020        continue;
2021      }
2022      RegionInfo hri = h.getRegion();
2023      if (hri == null) {
2024        LOG.warn(
2025          "Unable to close region " + hi.getRegionNameAsString()
2026            + " because {} had invalid or missing " + HConstants.CATALOG_FAMILY_STR + ":"
2027            + Bytes.toString(HConstants.REGIONINFO_QUALIFIER) + " qualifier value.",
2028          TableName.META_TABLE_NAME);
2029        continue;
2030      }
2031      // close the region -- close files and remove assignment
2032      HBaseFsckRepair.closeRegionSilentlyAndWait(connection, serverName, hri);
2033    }
2034  }
2035
2036  private void undeployRegions(HbckRegionInfo hi) throws IOException, InterruptedException {
2037    undeployRegionsForHbi(hi);
2038    // undeploy replicas of the region (but only if the method is invoked for the primary)
2039    if (hi.getReplicaId() != RegionInfo.DEFAULT_REPLICA_ID) {
2040      return;
2041    }
2042    int numReplicas = admin.getDescriptor(hi.getTableName()).getRegionReplication();
2043    for (int i = 1; i < numReplicas; i++) {
2044      if (hi.getPrimaryHRIForDeployedReplica() == null) continue;
2045      RegionInfo hri =
2046        RegionReplicaUtil.getRegionInfoForReplica(hi.getPrimaryHRIForDeployedReplica(), i);
2047      HbckRegionInfo h = regionInfoMap.get(hri.getEncodedName());
2048      if (h != null) {
2049        undeployRegionsForHbi(h);
2050        // set skip checks; we undeployed it, and we don't want to evaluate this anymore
2051        // in consistency checks
2052        h.setSkipChecks(true);
2053      }
2054    }
2055  }
2056
2057  private void undeployRegionsForHbi(HbckRegionInfo hi) throws IOException, InterruptedException {
2058    for (HbckRegionInfo.OnlineEntry rse : hi.getOnlineEntries()) {
2059      LOG.debug("Undeploy region " + rse.getRegionInfo() + " from " + rse.getServerName());
2060      try {
2061        HBaseFsckRepair.closeRegionSilentlyAndWait(connection, rse.getServerName(),
2062          rse.getRegionInfo());
2063        offline(rse.getRegionInfo().getRegionName());
2064      } catch (IOException ioe) {
2065        LOG.warn("Got exception when attempting to offline region "
2066          + Bytes.toString(rse.getRegionInfo().getRegionName()), ioe);
2067      }
2068    }
2069  }
2070
2071  private void tryAssignmentRepair(HbckRegionInfo hbi, String msg)
2072    throws IOException, KeeperException, InterruptedException {
2073    // If we are trying to fix the errors
2074    if (shouldFixAssignments()) {
2075      errors.print(msg);
2076      undeployRegions(hbi);
2077      setShouldRerun();
2078      RegionInfo hri = hbi.getHdfsHRI();
2079      if (hri == null) {
2080        hri = hbi.getMetaEntry().getRegionInfo();
2081      }
2082      HBaseFsckRepair.fixUnassigned(admin, hri);
2083      HBaseFsckRepair.waitUntilAssigned(admin, hri);
2084
2085      // also assign replicas if needed (do it only when this call operates on a primary replica)
2086      if (hbi.getReplicaId() != RegionInfo.DEFAULT_REPLICA_ID) return;
2087      int replicationCount = admin.getDescriptor(hri.getTable()).getRegionReplication();
2088      for (int i = 1; i < replicationCount; i++) {
2089        hri = RegionReplicaUtil.getRegionInfoForReplica(hri, i);
2090        HbckRegionInfo h = regionInfoMap.get(hri.getEncodedName());
2091        if (h != null) {
2092          undeployRegions(h);
2093          // set skip checks; we undeploy & deploy it; we don't want to evaluate this hbi anymore
2094          // in consistency checks
2095          h.setSkipChecks(true);
2096        }
2097        HBaseFsckRepair.fixUnassigned(admin, hri);
2098        HBaseFsckRepair.waitUntilAssigned(admin, hri);
2099      }
2100
2101    }
2102  }
2103
2104  /**
2105   * Check a single region for consistency and correct deployment.
2106   */
2107  private void checkRegionConsistency(final String key, final HbckRegionInfo hbi)
2108    throws IOException, KeeperException, InterruptedException {
2109
2110    if (hbi.isSkipChecks()) return;
2111    String descriptiveName = hbi.toString();
2112    boolean inMeta = hbi.getMetaEntry() != null;
2113    // In case not checking HDFS, assume the region is on HDFS
2114    boolean inHdfs = !shouldCheckHdfs() || hbi.getHdfsRegionDir() != null;
2115    boolean hasMetaAssignment = inMeta && hbi.getMetaEntry().regionServer != null;
2116    boolean isDeployed = !hbi.getDeployedOn().isEmpty();
2117    boolean isMultiplyDeployed = hbi.getDeployedOn().size() > 1;
2118    boolean deploymentMatchesMeta = hasMetaAssignment && isDeployed && !isMultiplyDeployed
2119      && hbi.getMetaEntry().regionServer.equals(hbi.getDeployedOn().get(0));
2120    boolean splitParent = inMeta && hbi.getMetaEntry().getRegionInfo().isSplit()
2121      && hbi.getMetaEntry().getRegionInfo().isOffline();
2122    boolean shouldBeDeployed =
2123      inMeta && !isTableDisabled(hbi.getMetaEntry().getRegionInfo().getTable());
2124    boolean recentlyModified =
2125      inHdfs && hbi.getModTime() + timelag > EnvironmentEdgeManager.currentTime();
2126
2127    // ========== First the healthy cases =============
2128    if (hbi.containsOnlyHdfsEdits()) {
2129      return;
2130    }
2131    if (inMeta && inHdfs && isDeployed && deploymentMatchesMeta && shouldBeDeployed) {
2132      return;
2133    } else if (inMeta && inHdfs && !shouldBeDeployed && !isDeployed) {
2134      LOG.info("Region " + descriptiveName + " is in META, and in a disabled "
2135        + "tabled that is not deployed");
2136      return;
2137    } else if (recentlyModified) {
2138      LOG.warn("Region " + descriptiveName + " was recently modified -- skipping");
2139      return;
2140    }
2141    // ========== Cases where the region is not in hbase:meta =============
2142    else if (!inMeta && !inHdfs && !isDeployed) {
2143      // We shouldn't have record of this region at all then!
2144      assert false : "Entry for region with no data";
2145    } else if (!inMeta && !inHdfs && isDeployed) {
2146      errors.reportError(ERROR_CODE.NOT_IN_META_HDFS,
2147        "Region " + descriptiveName + ", key=" + key + ", not on HDFS or in "
2148          + TableName.META_TABLE_NAME + " but " + "deployed on "
2149          + Joiner.on(", ").join(hbi.getDeployedOn()));
2150      if (shouldFixAssignments()) {
2151        undeployRegions(hbi);
2152      }
2153
2154    } else if (!inMeta && inHdfs && !isDeployed) {
2155      if (hbi.isMerged()) {
2156        // This region has already been merged, the remaining hdfs file will be
2157        // cleaned by CatalogJanitor later
2158        hbi.setSkipChecks(true);
2159        LOG.info("Region " + descriptiveName
2160          + " got merge recently, its file(s) will be cleaned by CatalogJanitor later");
2161        return;
2162      }
2163      errors.reportError(ERROR_CODE.NOT_IN_META_OR_DEPLOYED,
2164        "Region " + descriptiveName + " on HDFS, but not listed in " + TableName.META_TABLE_NAME
2165          + " or deployed on any region server");
2166      // restore region consistency of an adopted orphan
2167      if (shouldFixMeta()) {
2168        if (!hbi.isHdfsRegioninfoPresent()) {
2169          LOG.error("Region " + hbi.getHdfsHRI() + " could have been repaired"
2170            + " in table integrity repair phase if -fixHdfsOrphans was" + " used.");
2171          return;
2172        }
2173
2174        RegionInfo hri = hbi.getHdfsHRI();
2175        HbckTableInfo tableInfo = tablesInfo.get(hri.getTable());
2176
2177        for (RegionInfo region : tableInfo.getRegionsFromMeta(this.regionInfoMap)) {
2178          if (
2179            Bytes.compareTo(region.getStartKey(), hri.getStartKey()) <= 0
2180              && (region.getEndKey().length == 0
2181                || Bytes.compareTo(region.getEndKey(), hri.getEndKey()) >= 0)
2182              && Bytes.compareTo(region.getStartKey(), hri.getEndKey()) <= 0
2183          ) {
2184            if (region.isSplit() || region.isOffline()) continue;
2185            Path regionDir = hbi.getHdfsRegionDir();
2186            FileSystem fs = regionDir.getFileSystem(getConf());
2187            List<Path> familyDirs = FSUtils.getFamilyDirs(fs, regionDir);
2188            for (Path familyDir : familyDirs) {
2189              List<Path> referenceFilePaths = FSUtils.getReferenceFilePaths(fs, familyDir);
2190              for (Path referenceFilePath : referenceFilePaths) {
2191                Path parentRegionDir =
2192                  StoreFileInfo.getReferredToFile(referenceFilePath).getParent().getParent();
2193                if (parentRegionDir.toString().endsWith(region.getEncodedName())) {
2194                  LOG.warn(hri + " start and stop keys are in the range of " + region
2195                    + ". The region might not be cleaned up from hdfs when region " + region
2196                    + " split failed. Hence deleting from hdfs.");
2197                  HRegionFileSystem.deleteRegionFromFileSystem(getConf(), fs, regionDir.getParent(),
2198                    hri);
2199                  return;
2200                }
2201              }
2202            }
2203          }
2204        }
2205        LOG.info("Patching {} with .regioninfo: " + hbi.getHdfsHRI(), TableName.META_TABLE_NAME);
2206        int numReplicas = admin.getDescriptor(hbi.getTableName()).getRegionReplication();
2207        HBaseFsckRepair.fixMetaHoleOnlineAndAddReplicas(getConf(), hbi.getHdfsHRI(),
2208          admin.getClusterMetrics(EnumSet.of(Option.LIVE_SERVERS)).getLiveServerMetrics().keySet(),
2209          numReplicas);
2210
2211        tryAssignmentRepair(hbi, "Trying to reassign region...");
2212      }
2213
2214    } else if (!inMeta && inHdfs && isDeployed) {
2215      errors.reportError(ERROR_CODE.NOT_IN_META, "Region " + descriptiveName
2216        + " not in META, but deployed on " + Joiner.on(", ").join(hbi.getDeployedOn()));
2217      debugLsr(hbi.getHdfsRegionDir());
2218      if (hbi.getReplicaId() != RegionInfo.DEFAULT_REPLICA_ID) {
2219        // for replicas, this means that we should undeploy the region (we would have
2220        // gone over the primaries and fixed meta holes in first phase under
2221        // checkAndFixConsistency; we shouldn't get the condition !inMeta at
2222        // this stage unless unwanted replica)
2223        if (shouldFixAssignments()) {
2224          undeployRegionsForHbi(hbi);
2225        }
2226      }
2227      if (shouldFixMeta() && hbi.getReplicaId() == RegionInfo.DEFAULT_REPLICA_ID) {
2228        if (!hbi.isHdfsRegioninfoPresent()) {
2229          LOG.error("This should have been repaired in table integrity repair phase");
2230          return;
2231        }
2232
2233        LOG.info("Patching {} with with .regioninfo: " + hbi.getHdfsHRI(),
2234          TableName.META_TABLE_NAME);
2235        int numReplicas = admin.getDescriptor(hbi.getTableName()).getRegionReplication();
2236        HBaseFsckRepair.fixMetaHoleOnlineAndAddReplicas(getConf(), hbi.getHdfsHRI(),
2237          admin.getClusterMetrics(EnumSet.of(Option.LIVE_SERVERS)).getLiveServerMetrics().keySet(),
2238          numReplicas);
2239        tryAssignmentRepair(hbi, "Trying to fix unassigned region...");
2240      }
2241
2242      // ========== Cases where the region is in hbase:meta =============
2243    } else if (inMeta && inHdfs && !isDeployed && splitParent) {
2244      // check whether this is an actual error, or just transient state where parent
2245      // is not cleaned
2246      if (hbi.getMetaEntry().splitA != null && hbi.getMetaEntry().splitB != null) {
2247        // check that split daughters are there
2248        HbckRegionInfo infoA = this.regionInfoMap.get(hbi.getMetaEntry().splitA.getEncodedName());
2249        HbckRegionInfo infoB = this.regionInfoMap.get(hbi.getMetaEntry().splitB.getEncodedName());
2250        if (infoA != null && infoB != null) {
2251          // we already processed or will process daughters. Move on, nothing to see here.
2252          hbi.setSkipChecks(true);
2253          return;
2254        }
2255      }
2256
2257      // For Replica region, we need to do a similar check. If replica is not split successfully,
2258      // error is going to be reported against primary daughter region.
2259      if (hbi.getReplicaId() != RegionInfo.DEFAULT_REPLICA_ID) {
2260        LOG.info("Region " + descriptiveName + " is a split parent in META, in HDFS, "
2261          + "and not deployed on any region server. This may be transient.");
2262        hbi.setSkipChecks(true);
2263        return;
2264      }
2265
2266      errors.reportError(ERROR_CODE.LINGERING_SPLIT_PARENT,
2267        "Region " + descriptiveName + " is a split parent in META, in HDFS, "
2268          + "and not deployed on any region server. This could be transient, "
2269          + "consider to run the catalog janitor first!");
2270      if (shouldFixSplitParents()) {
2271        setShouldRerun();
2272        resetSplitParent(hbi);
2273      }
2274    } else if (inMeta && !inHdfs && !isDeployed) {
2275      errors.reportError(ERROR_CODE.NOT_IN_HDFS_OR_DEPLOYED, "Region " + descriptiveName
2276        + " found in META, but not in HDFS " + "or deployed on any region server.");
2277      if (shouldFixMeta()) {
2278        deleteMetaRegion(hbi);
2279      }
2280    } else if (inMeta && !inHdfs && isDeployed) {
2281      errors.reportError(ERROR_CODE.NOT_IN_HDFS,
2282        "Region " + descriptiveName + " found in META, but not in HDFS, " + "and deployed on "
2283          + Joiner.on(", ").join(hbi.getDeployedOn()));
2284      // We treat HDFS as ground truth. Any information in meta is transient
2285      // and equivalent data can be regenerated. So, lets unassign and remove
2286      // these problems from META.
2287      if (shouldFixAssignments()) {
2288        errors.print("Trying to fix unassigned region...");
2289        undeployRegions(hbi);
2290      }
2291      if (shouldFixMeta()) {
2292        // wait for it to complete
2293        deleteMetaRegion(hbi);
2294      }
2295    } else if (inMeta && inHdfs && !isDeployed && shouldBeDeployed) {
2296      errors.reportError(ERROR_CODE.NOT_DEPLOYED,
2297        "Region " + descriptiveName + " not deployed on any region server.");
2298      tryAssignmentRepair(hbi, "Trying to fix unassigned region...");
2299    } else if (inMeta && inHdfs && isDeployed && !shouldBeDeployed) {
2300      errors.reportError(ERROR_CODE.SHOULD_NOT_BE_DEPLOYED,
2301        "Region " + descriptiveName + " should not be deployed according "
2302          + "to META, but is deployed on " + Joiner.on(", ").join(hbi.getDeployedOn()));
2303      if (shouldFixAssignments()) {
2304        errors.print("Trying to close the region " + descriptiveName);
2305        setShouldRerun();
2306        HBaseFsckRepair.fixMultiAssignment(connection, hbi.getMetaEntry().getRegionInfo(),
2307          hbi.getDeployedOn());
2308      }
2309    } else if (inMeta && inHdfs && isMultiplyDeployed) {
2310      errors.reportError(ERROR_CODE.MULTI_DEPLOYED,
2311        "Region " + descriptiveName + " is listed in " + TableName.META_TABLE_NAME
2312          + " on region server " + hbi.getMetaEntry().regionServer + " but is multiply assigned"
2313          + " to region servers " + Joiner.on(", ").join(hbi.getDeployedOn()));
2314      // If we are trying to fix the errors
2315      if (shouldFixAssignments()) {
2316        errors.print("Trying to fix assignment error...");
2317        setShouldRerun();
2318        HBaseFsckRepair.fixMultiAssignment(connection, hbi.getMetaEntry().getRegionInfo(),
2319          hbi.getDeployedOn());
2320      }
2321    } else if (inMeta && inHdfs && isDeployed && !deploymentMatchesMeta) {
2322      errors.reportError(ERROR_CODE.SERVER_DOES_NOT_MATCH_META,
2323        "Region " + descriptiveName + " listed in " + TableName.META_TABLE_NAME
2324          + " on region server " + hbi.getMetaEntry().regionServer + " but found on region server "
2325          + hbi.getDeployedOn().get(0));
2326      // If we are trying to fix the errors
2327      if (shouldFixAssignments()) {
2328        errors.print("Trying to fix assignment error...");
2329        setShouldRerun();
2330        HBaseFsckRepair.fixMultiAssignment(connection, hbi.getMetaEntry().getRegionInfo(),
2331          hbi.getDeployedOn());
2332        HBaseFsckRepair.waitUntilAssigned(admin, hbi.getHdfsHRI());
2333      }
2334    } else {
2335      errors.reportError(ERROR_CODE.UNKNOWN,
2336        "Region " + descriptiveName + " is in an unforeseen state:" + " inMeta=" + inMeta
2337          + " inHdfs=" + inHdfs + " isDeployed=" + isDeployed + " isMultiplyDeployed="
2338          + isMultiplyDeployed + " deploymentMatchesMeta=" + deploymentMatchesMeta
2339          + " shouldBeDeployed=" + shouldBeDeployed);
2340    }
2341  }
2342
2343  /**
2344   * Checks tables integrity. Goes over all regions and scans the tables. Collects all the pieces
2345   * for each table and checks if there are missing, repeated or overlapping ones.
2346   */
2347  SortedMap<TableName, HbckTableInfo> checkIntegrity() throws IOException {
2348    tablesInfo = new TreeMap<>();
2349    LOG.debug("There are " + regionInfoMap.size() + " region info entries");
2350    for (HbckRegionInfo hbi : regionInfoMap.values()) {
2351      // Check only valid, working regions
2352      if (hbi.getMetaEntry() == null) {
2353        // this assumes that consistency check has run loadMetaEntry
2354        Path p = hbi.getHdfsRegionDir();
2355        if (p == null) {
2356          errors.report("No regioninfo in Meta or HDFS. " + hbi);
2357        }
2358
2359        // TODO test.
2360        continue;
2361      }
2362      if (hbi.getMetaEntry().regionServer == null) {
2363        errors.detail("Skipping region because no region server: " + hbi);
2364        continue;
2365      }
2366      if (hbi.getMetaEntry().getRegionInfo().isOffline()) {
2367        errors.detail("Skipping region because it is offline: " + hbi);
2368        continue;
2369      }
2370      if (hbi.containsOnlyHdfsEdits()) {
2371        errors.detail("Skipping region because it only contains edits" + hbi);
2372        continue;
2373      }
2374
2375      // Missing regionDir or over-deployment is checked elsewhere. Include
2376      // these cases in modTInfo, so we can evaluate those regions as part of
2377      // the region chain in META
2378      // if (hbi.foundRegionDir == null) continue;
2379      // if (hbi.deployedOn.size() != 1) continue;
2380      if (hbi.getDeployedOn().isEmpty()) {
2381        continue;
2382      }
2383
2384      // We should be safe here
2385      TableName tableName = hbi.getMetaEntry().getRegionInfo().getTable();
2386      HbckTableInfo modTInfo = tablesInfo.get(tableName);
2387      if (modTInfo == null) {
2388        modTInfo = new HbckTableInfo(tableName, this);
2389      }
2390      for (ServerName server : hbi.getDeployedOn()) {
2391        modTInfo.addServer(server);
2392      }
2393
2394      if (!hbi.isSkipChecks()) {
2395        modTInfo.addRegionInfo(hbi);
2396      }
2397
2398      tablesInfo.put(tableName, modTInfo);
2399    }
2400
2401    loadTableInfosForTablesWithNoRegion();
2402
2403    logParallelMerge();
2404    for (HbckTableInfo tInfo : tablesInfo.values()) {
2405      TableIntegrityErrorHandler handler = tInfo.new IntegrityFixSuggester(tInfo, errors);
2406      if (!tInfo.checkRegionChain(handler)) {
2407        errors.report("Found inconsistency in table " + tInfo.getName());
2408      }
2409    }
2410    return tablesInfo;
2411  }
2412
2413  /**
2414   * Loads table info's for tables that may not have been included, since there are no regions
2415   * reported for the table, but table dir is there in hdfs
2416   */
2417  private void loadTableInfosForTablesWithNoRegion() throws IOException {
2418    Map<String, TableDescriptor> allTables = new FSTableDescriptors(getConf()).getAll();
2419    for (TableDescriptor htd : allTables.values()) {
2420      if (checkMetaOnly && !htd.isMetaTable()) {
2421        continue;
2422      }
2423
2424      TableName tableName = htd.getTableName();
2425      if (isTableIncluded(tableName) && !tablesInfo.containsKey(tableName)) {
2426        HbckTableInfo tableInfo = new HbckTableInfo(tableName, this);
2427        tableInfo.htds.add(htd);
2428        tablesInfo.put(htd.getTableName(), tableInfo);
2429      }
2430    }
2431  }
2432
2433  /**
2434   * Merge hdfs data by moving from contained HbckRegionInfo into targetRegionDir.
2435   * @return number of file move fixes done to merge regions.
2436   */
2437  public int mergeRegionDirs(Path targetRegionDir, HbckRegionInfo contained) throws IOException {
2438    int fileMoves = 0;
2439    String thread = Thread.currentThread().getName();
2440    LOG.debug("[" + thread + "] Contained region dir after close and pause");
2441    debugLsr(contained.getHdfsRegionDir());
2442
2443    // rename the contained into the container.
2444    FileSystem fs = targetRegionDir.getFileSystem(getConf());
2445    FileStatus[] dirs = null;
2446    try {
2447      dirs = fs.listStatus(contained.getHdfsRegionDir());
2448    } catch (FileNotFoundException fnfe) {
2449      // region we are attempting to merge in is not present! Since this is a merge, there is
2450      // no harm skipping this region if it does not exist.
2451      if (!fs.exists(contained.getHdfsRegionDir())) {
2452        LOG.warn("[" + thread + "] HDFS region dir " + contained.getHdfsRegionDir()
2453          + " is missing. Assuming already sidelined or moved.");
2454      } else {
2455        sidelineRegionDir(fs, contained);
2456      }
2457      return fileMoves;
2458    }
2459
2460    if (dirs == null) {
2461      if (!fs.exists(contained.getHdfsRegionDir())) {
2462        LOG.warn("[" + thread + "] HDFS region dir " + contained.getHdfsRegionDir()
2463          + " already sidelined.");
2464      } else {
2465        sidelineRegionDir(fs, contained);
2466      }
2467      return fileMoves;
2468    }
2469
2470    for (FileStatus cf : dirs) {
2471      Path src = cf.getPath();
2472      Path dst = new Path(targetRegionDir, src.getName());
2473
2474      if (src.getName().equals(HRegionFileSystem.REGION_INFO_FILE)) {
2475        // do not copy the old .regioninfo file.
2476        continue;
2477      }
2478
2479      if (src.getName().equals(HConstants.HREGION_OLDLOGDIR_NAME)) {
2480        // do not copy the .oldlogs files
2481        continue;
2482      }
2483
2484      LOG.info("[" + thread + "] Moving files from " + src + " into containing region " + dst);
2485      // FileSystem.rename is inconsistent with directories -- if the
2486      // dst (foo/a) exists and is a dir, and the src (foo/b) is a dir,
2487      // it moves the src into the dst dir resulting in (foo/a/b). If
2488      // the dst does not exist, and the src a dir, src becomes dst. (foo/b)
2489      for (FileStatus hfile : fs.listStatus(src)) {
2490        boolean success = fs.rename(hfile.getPath(), dst);
2491        if (success) {
2492          fileMoves++;
2493        }
2494      }
2495      LOG.debug("[" + thread + "] Sideline directory contents:");
2496      debugLsr(targetRegionDir);
2497    }
2498
2499    // if all success.
2500    sidelineRegionDir(fs, contained);
2501    LOG.info("[" + thread + "] Sidelined region dir " + contained.getHdfsRegionDir() + " into "
2502      + getSidelineDir());
2503    debugLsr(contained.getHdfsRegionDir());
2504
2505    return fileMoves;
2506  }
2507
2508  static class WorkItemOverlapMerge implements Callable<Void> {
2509    private TableIntegrityErrorHandler handler;
2510    Collection<HbckRegionInfo> overlapgroup;
2511
2512    WorkItemOverlapMerge(Collection<HbckRegionInfo> overlapgroup,
2513      TableIntegrityErrorHandler handler) {
2514      this.handler = handler;
2515      this.overlapgroup = overlapgroup;
2516    }
2517
2518    @Override
2519    public Void call() throws Exception {
2520      handler.handleOverlapGroup(overlapgroup);
2521      return null;
2522    }
2523  }
2524
2525  /**
2526   * Return a list of user-space table names whose metadata have not been modified in the last few
2527   * milliseconds specified by timelag if any of the REGIONINFO_QUALIFIER, SERVER_QUALIFIER,
2528   * STARTCODE_QUALIFIER, SPLITA_QUALIFIER, SPLITB_QUALIFIER have not changed in the last
2529   * milliseconds specified by timelag, then the table is a candidate to be returned.
2530   * @return tables that have not been modified recently
2531   * @throws IOException if an error is encountered
2532   */
2533  TableDescriptor[] getTables(AtomicInteger numSkipped) {
2534    List<TableName> tableNames = new ArrayList<>();
2535    long now = EnvironmentEdgeManager.currentTime();
2536
2537    for (HbckRegionInfo hbi : regionInfoMap.values()) {
2538      HbckRegionInfo.MetaEntry info = hbi.getMetaEntry();
2539
2540      // if the start key is zero, then we have found the first region of a table.
2541      // pick only those tables that were not modified in the last few milliseconds.
2542      if (
2543        info != null && info.getRegionInfo().getStartKey().length == 0
2544          && !info.getRegionInfo().isMetaRegion()
2545      ) {
2546        if (info.modTime + timelag < now) {
2547          tableNames.add(info.getRegionInfo().getTable());
2548        } else {
2549          numSkipped.incrementAndGet(); // one more in-flux table
2550        }
2551      }
2552    }
2553    return getTableDescriptors(tableNames);
2554  }
2555
2556  TableDescriptor[] getTableDescriptors(List<TableName> tableNames) {
2557    LOG.info("getTableDescriptors == tableNames => " + tableNames);
2558    try (Connection conn = ConnectionFactory.createConnection(getConf());
2559      Admin admin = conn.getAdmin()) {
2560      List<TableDescriptor> tds = admin.listTableDescriptors(tableNames);
2561      return tds.toArray(new TableDescriptor[tds.size()]);
2562    } catch (IOException e) {
2563      LOG.debug("Exception getting table descriptors", e);
2564    }
2565    return new TableDescriptor[0];
2566  }
2567
2568  /**
2569   * Gets the entry in regionInfo corresponding to the the given encoded region name. If the region
2570   * has not been seen yet, a new entry is added and returned.
2571   */
2572  private synchronized HbckRegionInfo getOrCreateInfo(String name) {
2573    HbckRegionInfo hbi = regionInfoMap.get(name);
2574    if (hbi == null) {
2575      hbi = new HbckRegionInfo(null);
2576      regionInfoMap.put(name, hbi);
2577    }
2578    return hbi;
2579  }
2580
2581  private void checkAndFixReplication() throws ReplicationException, IOException {
2582    ReplicationChecker checker = new ReplicationChecker(getConf(), zkw, connection, errors);
2583
2584    if (!checker.checkHasDataInQueues()) {
2585      return;
2586    }
2587
2588    checker.checkUnDeletedQueues();
2589
2590    if (checker.hasUnDeletedQueues() && this.fixReplication) {
2591      checker.fixUnDeletedQueues();
2592      setShouldRerun();
2593    }
2594  }
2595
2596  /**
2597   * Check values in regionInfo for hbase:meta Check if zero or more than one regions with
2598   * hbase:meta are found. If there are inconsistencies (i.e. zero or more than one regions pretend
2599   * to be holding the hbase:meta) try to fix that and report an error.
2600   * @throws IOException from HBaseFsckRepair functions
2601   */
2602  boolean checkMetaRegion() throws IOException, KeeperException, InterruptedException {
2603    Map<Integer, HbckRegionInfo> metaRegions = new HashMap<>();
2604    for (HbckRegionInfo value : regionInfoMap.values()) {
2605      if (value.getMetaEntry() != null && value.getMetaEntry().getRegionInfo().isMetaRegion()) {
2606        metaRegions.put(value.getReplicaId(), value);
2607      }
2608    }
2609    int metaReplication = admin.getDescriptor(TableName.META_TABLE_NAME).getRegionReplication();
2610    boolean noProblem = true;
2611    // There will be always entries in regionInfoMap corresponding to hbase:meta & its replicas
2612    // Check the deployed servers. It should be exactly one server for each replica.
2613    for (int i = 0; i < metaReplication; i++) {
2614      HbckRegionInfo metaHbckRegionInfo = metaRegions.remove(i);
2615      List<ServerName> servers = new ArrayList<>();
2616      if (metaHbckRegionInfo != null) {
2617        servers = metaHbckRegionInfo.getDeployedOn();
2618      }
2619      if (servers.size() != 1) {
2620        noProblem = false;
2621        if (servers.isEmpty()) {
2622          assignMetaReplica(i);
2623        } else if (servers.size() > 1) {
2624          errors.reportError(ERROR_CODE.MULTI_META_REGION,
2625            TableName.META_TABLE_NAME + ", replicaId " + metaHbckRegionInfo.getReplicaId()
2626              + " is found on more than one region.");
2627          if (shouldFixAssignments()) {
2628            errors.print("Trying to fix a problem with " + TableName.META_TABLE_NAME
2629              + ", replicaId " + metaHbckRegionInfo.getReplicaId() + "..");
2630            setShouldRerun();
2631            // try fix it (treat is a dupe assignment)
2632            HBaseFsckRepair.fixMultiAssignment(connection,
2633              metaHbckRegionInfo.getMetaEntry().getRegionInfo(), servers);
2634          }
2635        }
2636      }
2637    }
2638    // unassign whatever is remaining in metaRegions. They are excess replicas.
2639    for (Map.Entry<Integer, HbckRegionInfo> entry : metaRegions.entrySet()) {
2640      noProblem = false;
2641      errors.reportError(ERROR_CODE.SHOULD_NOT_BE_DEPLOYED,
2642        TableName.META_TABLE_NAME + " replicas are deployed in excess. Configured "
2643          + metaReplication + ", deployed " + metaRegions.size());
2644      if (shouldFixAssignments()) {
2645        errors.print("Trying to undeploy excess replica, replicaId: " + entry.getKey() + " of "
2646          + TableName.META_TABLE_NAME + "..");
2647        setShouldRerun();
2648        unassignMetaReplica(entry.getValue());
2649      }
2650    }
2651    // if noProblem is false, rerun hbck with hopefully fixed META
2652    // if noProblem is true, no errors, so continue normally
2653    return noProblem;
2654  }
2655
2656  private void unassignMetaReplica(HbckRegionInfo hi)
2657    throws IOException, InterruptedException, KeeperException {
2658    undeployRegions(hi);
2659    ZKUtil.deleteNode(zkw,
2660      zkw.getZNodePaths().getZNodeForReplica(hi.getMetaEntry().getRegionInfo().getReplicaId()));
2661  }
2662
2663  private void assignMetaReplica(int replicaId)
2664    throws IOException, KeeperException, InterruptedException {
2665    errors.reportError(ERROR_CODE.NO_META_REGION,
2666      TableName.META_TABLE_NAME + ", replicaId " + replicaId + " is not found on any region.");
2667    if (shouldFixAssignments()) {
2668      errors.print("Trying to fix a problem with " + TableName.META_TABLE_NAME + "..");
2669      setShouldRerun();
2670      // try to fix it (treat it as unassigned region)
2671      RegionInfo h = RegionReplicaUtil
2672        .getRegionInfoForReplica(RegionInfoBuilder.FIRST_META_REGIONINFO, replicaId);
2673      HBaseFsckRepair.fixUnassigned(admin, h);
2674      HBaseFsckRepair.waitUntilAssigned(admin, h);
2675    }
2676  }
2677
2678  /**
2679   * Scan hbase:meta, adding all regions found to the regionInfo map.
2680   * @throws IOException if an error is encountered
2681   */
2682  boolean loadMetaEntries() throws IOException {
2683    ClientMetaTableAccessor.Visitor visitor = new ClientMetaTableAccessor.Visitor() {
2684      int countRecord = 1;
2685
2686      // comparator to sort KeyValues with latest modtime
2687      final Comparator<Cell> comp = new Comparator<Cell>() {
2688        @Override
2689        public int compare(Cell k1, Cell k2) {
2690          return Long.compare(k1.getTimestamp(), k2.getTimestamp());
2691        }
2692      };
2693
2694      @Override
2695      public boolean visit(Result result) throws IOException {
2696        try {
2697
2698          // record the latest modification of this META record
2699          long ts = Collections.max(result.listCells(), comp).getTimestamp();
2700          RegionLocations rl = CatalogFamilyFormat.getRegionLocations(result);
2701          if (rl == null) {
2702            emptyRegionInfoQualifiers.add(result);
2703            errors.reportError(ERROR_CODE.EMPTY_META_CELL,
2704              "Empty REGIONINFO_QUALIFIER found in " + TableName.META_TABLE_NAME);
2705            return true;
2706          }
2707          ServerName sn = null;
2708          if (
2709            rl.getRegionLocation(RegionInfo.DEFAULT_REPLICA_ID) == null
2710              || rl.getRegionLocation(RegionInfo.DEFAULT_REPLICA_ID).getRegion() == null
2711          ) {
2712            emptyRegionInfoQualifiers.add(result);
2713            errors.reportError(ERROR_CODE.EMPTY_META_CELL,
2714              "Empty REGIONINFO_QUALIFIER found in " + TableName.META_TABLE_NAME);
2715            return true;
2716          }
2717          RegionInfo hri = rl.getRegionLocation(RegionInfo.DEFAULT_REPLICA_ID).getRegion();
2718          if (!(isTableIncluded(hri.getTable()) || hri.isMetaRegion())) {
2719            return true;
2720          }
2721          PairOfSameType<RegionInfo> daughters = MetaTableAccessor.getDaughterRegions(result);
2722          for (HRegionLocation h : rl.getRegionLocations()) {
2723            if (h == null || h.getRegion() == null) {
2724              continue;
2725            }
2726            sn = h.getServerName();
2727            hri = h.getRegion();
2728
2729            HbckRegionInfo.MetaEntry m = null;
2730            if (hri.getReplicaId() == RegionInfo.DEFAULT_REPLICA_ID) {
2731              m = new HbckRegionInfo.MetaEntry(hri, sn, ts, daughters.getFirst(),
2732                daughters.getSecond());
2733            } else {
2734              m = new HbckRegionInfo.MetaEntry(hri, sn, ts, null, null);
2735            }
2736            HbckRegionInfo previous = regionInfoMap.get(hri.getEncodedName());
2737            if (previous == null) {
2738              regionInfoMap.put(hri.getEncodedName(), new HbckRegionInfo(m));
2739            } else if (previous.getMetaEntry() == null) {
2740              previous.setMetaEntry(m);
2741            } else {
2742              throw new IOException(
2743                "Two entries in " + TableName.META_TABLE_NAME + " are same " + previous);
2744            }
2745          }
2746          List<RegionInfo> mergeParents = CatalogFamilyFormat.getMergeRegions(result.rawCells());
2747          if (mergeParents != null) {
2748            for (RegionInfo mergeRegion : mergeParents) {
2749              if (mergeRegion != null) {
2750                // This region is already being merged
2751                HbckRegionInfo hbInfo = getOrCreateInfo(mergeRegion.getEncodedName());
2752                hbInfo.setMerged(true);
2753              }
2754            }
2755          }
2756
2757          // show proof of progress to the user, once for every 100 records.
2758          if (countRecord % 100 == 0) {
2759            errors.progress();
2760          }
2761          countRecord++;
2762          return true;
2763        } catch (RuntimeException e) {
2764          LOG.error("Result=" + result);
2765          throw e;
2766        }
2767      }
2768    };
2769    if (!checkMetaOnly) {
2770      // Scan hbase:meta to pick up user regions
2771      MetaTableAccessor.fullScanRegions(connection, visitor);
2772    }
2773
2774    errors.print("");
2775    return true;
2776  }
2777
2778  /**
2779   * Prints summary of all tables found on the system.
2780   */
2781  private void printTableSummary(SortedMap<TableName, HbckTableInfo> tablesInfo) {
2782    StringBuilder sb = new StringBuilder();
2783    int numOfSkippedRegions;
2784    errors.print("Summary:");
2785    for (HbckTableInfo tInfo : tablesInfo.values()) {
2786      numOfSkippedRegions = (skippedRegions.containsKey(tInfo.getName()))
2787        ? skippedRegions.get(tInfo.getName()).size()
2788        : 0;
2789
2790      if (errors.tableHasErrors(tInfo)) {
2791        errors.print("Table " + tInfo.getName() + " is inconsistent.");
2792      } else if (numOfSkippedRegions > 0) {
2793        errors.print("Table " + tInfo.getName() + " is okay (with " + numOfSkippedRegions
2794          + " skipped regions).");
2795      } else {
2796        errors.print("Table " + tInfo.getName() + " is okay.");
2797      }
2798      errors.print("    Number of regions: " + tInfo.getNumRegions());
2799      if (numOfSkippedRegions > 0) {
2800        Set<String> skippedRegionStrings = skippedRegions.get(tInfo.getName());
2801        System.out.println("    Number of skipped regions: " + numOfSkippedRegions);
2802        System.out.println("      List of skipped regions:");
2803        for (String sr : skippedRegionStrings) {
2804          System.out.println("        " + sr);
2805        }
2806      }
2807      sb.setLength(0); // clear out existing buffer, if any.
2808      sb.append("    Deployed on: ");
2809      for (ServerName server : tInfo.deployedOn) {
2810        sb.append(" " + server.toString());
2811      }
2812      errors.print(sb.toString());
2813    }
2814  }
2815
2816  static HbckErrorReporter getErrorReporter(final Configuration conf)
2817    throws ClassNotFoundException {
2818    Class<? extends HbckErrorReporter> reporter = conf.getClass("hbasefsck.errorreporter",
2819      PrintingErrorReporter.class, HbckErrorReporter.class);
2820    return ReflectionUtils.newInstance(reporter, conf);
2821  }
2822
2823  static class PrintingErrorReporter implements HbckErrorReporter {
2824    public int errorCount = 0;
2825    private int showProgress;
2826    // How frequently calls to progress() will create output
2827    private static final int progressThreshold = 100;
2828
2829    Set<HbckTableInfo> errorTables = new HashSet<>();
2830
2831    // for use by unit tests to verify which errors were discovered
2832    private ArrayList<ERROR_CODE> errorList = new ArrayList<>();
2833
2834    @Override
2835    public void clear() {
2836      errorTables.clear();
2837      errorList.clear();
2838      errorCount = 0;
2839    }
2840
2841    @Override
2842    public synchronized void reportError(ERROR_CODE errorCode, String message) {
2843      if (errorCode == ERROR_CODE.WRONG_USAGE) {
2844        System.err.println(message);
2845        return;
2846      }
2847
2848      errorList.add(errorCode);
2849      if (!summary) {
2850        System.out.println("ERROR: " + message);
2851      }
2852      errorCount++;
2853      showProgress = 0;
2854    }
2855
2856    @Override
2857    public synchronized void reportError(ERROR_CODE errorCode, String message,
2858      HbckTableInfo table) {
2859      errorTables.add(table);
2860      reportError(errorCode, message);
2861    }
2862
2863    @Override
2864    public synchronized void reportError(ERROR_CODE errorCode, String message, HbckTableInfo table,
2865      HbckRegionInfo info) {
2866      errorTables.add(table);
2867      String reference = "(region " + info.getRegionNameAsString() + ")";
2868      reportError(errorCode, reference + " " + message);
2869    }
2870
2871    @Override
2872    public synchronized void reportError(ERROR_CODE errorCode, String message, HbckTableInfo table,
2873      HbckRegionInfo info1, HbckRegionInfo info2) {
2874      errorTables.add(table);
2875      String reference =
2876        "(regions " + info1.getRegionNameAsString() + " and " + info2.getRegionNameAsString() + ")";
2877      reportError(errorCode, reference + " " + message);
2878    }
2879
2880    @Override
2881    public synchronized void reportError(String message) {
2882      reportError(ERROR_CODE.UNKNOWN, message);
2883    }
2884
2885    /**
2886     * Report error information, but do not increment the error count. Intended for cases where the
2887     * actual error would have been reported previously.
2888     */
2889    @Override
2890    public synchronized void report(String message) {
2891      if (!summary) {
2892        System.out.println("ERROR: " + message);
2893      }
2894      showProgress = 0;
2895    }
2896
2897    @Override
2898    public synchronized int summarize() {
2899      System.out.println(Integer.toString(errorCount) + " inconsistencies detected.");
2900      if (errorCount == 0) {
2901        System.out.println("Status: OK");
2902        return 0;
2903      } else {
2904        System.out.println("Status: INCONSISTENT");
2905        return -1;
2906      }
2907    }
2908
2909    @Override
2910    public ArrayList<ERROR_CODE> getErrorList() {
2911      return errorList;
2912    }
2913
2914    @Override
2915    public synchronized void print(String message) {
2916      if (!summary) {
2917        System.out.println(message);
2918      }
2919    }
2920
2921    @Override
2922    public boolean tableHasErrors(HbckTableInfo table) {
2923      return errorTables.contains(table);
2924    }
2925
2926    @Override
2927    public void resetErrors() {
2928      errorCount = 0;
2929    }
2930
2931    @Override
2932    public synchronized void detail(String message) {
2933      if (details) {
2934        System.out.println(message);
2935      }
2936      showProgress = 0;
2937    }
2938
2939    @Override
2940    public synchronized void progress() {
2941      if (showProgress++ == progressThreshold) {
2942        if (!summary) {
2943          System.out.print(".");
2944        }
2945        showProgress = 0;
2946      }
2947    }
2948  }
2949
2950  /**
2951   * Contact a region server and get all information from it
2952   */
2953  static class WorkItemRegion implements Callable<Void> {
2954    private final HBaseFsck hbck;
2955    private final ServerName rsinfo;
2956    private final HbckErrorReporter errors;
2957    private final Connection connection;
2958
2959    WorkItemRegion(HBaseFsck hbck, ServerName info, HbckErrorReporter errors,
2960      Connection connection) {
2961      this.hbck = hbck;
2962      this.rsinfo = info;
2963      this.errors = errors;
2964      this.connection = connection;
2965    }
2966
2967    @Override
2968    public synchronized Void call() throws IOException {
2969      errors.progress();
2970      try {
2971        // list all online regions from this region server
2972        List<RegionInfo> regions = connection.getAdmin().getRegions(rsinfo);
2973        regions = filterRegions(regions);
2974
2975        if (details) {
2976          errors.detail(
2977            "RegionServer: " + rsinfo.getServerName() + " number of regions: " + regions.size());
2978          for (RegionInfo rinfo : regions) {
2979            errors.detail("  " + rinfo.getRegionNameAsString() + " id: " + rinfo.getRegionId()
2980              + " encoded_name: " + rinfo.getEncodedName() + " start: "
2981              + Bytes.toStringBinary(rinfo.getStartKey()) + " end: "
2982              + Bytes.toStringBinary(rinfo.getEndKey()));
2983          }
2984        }
2985
2986        // check to see if the existence of this region matches the region in META
2987        for (RegionInfo r : regions) {
2988          HbckRegionInfo hbi = hbck.getOrCreateInfo(r.getEncodedName());
2989          hbi.addServer(r, rsinfo);
2990        }
2991      } catch (IOException e) { // unable to connect to the region server.
2992        errors.reportError(ERROR_CODE.RS_CONNECT_FAILURE,
2993          "RegionServer: " + rsinfo.getServerName() + " Unable to fetch region information. " + e);
2994        throw e;
2995      }
2996      return null;
2997    }
2998
2999    private List<RegionInfo> filterRegions(List<RegionInfo> regions) {
3000      List<RegionInfo> ret = Lists.newArrayList();
3001      for (RegionInfo hri : regions) {
3002        if (hri.isMetaRegion() || (!hbck.checkMetaOnly && hbck.isTableIncluded(hri.getTable()))) {
3003          ret.add(hri);
3004        }
3005      }
3006      return ret;
3007    }
3008  }
3009
3010  /**
3011   * Contact hdfs and get all information about specified table directory into regioninfo list.
3012   */
3013  class WorkItemHdfsDir implements Callable<Void> {
3014    private FileStatus tableDir;
3015    private HbckErrorReporter errors;
3016    private FileSystem fs;
3017
3018    WorkItemHdfsDir(FileSystem fs, HbckErrorReporter errors, FileStatus status) {
3019      this.fs = fs;
3020      this.tableDir = status;
3021      this.errors = errors;
3022    }
3023
3024    @Override
3025    public synchronized Void call() throws InterruptedException, ExecutionException {
3026      final Vector<Exception> exceptions = new Vector<>();
3027
3028      try {
3029        final FileStatus[] regionDirs = fs.listStatus(tableDir.getPath());
3030        final List<Future<?>> futures = new ArrayList<>(regionDirs.length);
3031
3032        for (final FileStatus regionDir : regionDirs) {
3033          errors.progress();
3034          final String encodedName = regionDir.getPath().getName();
3035          // ignore directories that aren't hexadecimal
3036          if (!encodedName.toLowerCase(Locale.ROOT).matches("[0-9a-f]+")) {
3037            continue;
3038          }
3039
3040          if (!exceptions.isEmpty()) {
3041            break;
3042          }
3043
3044          futures.add(executor.submit(new Runnable() {
3045            @Override
3046            public void run() {
3047              try {
3048                LOG.debug("Loading region info from hdfs:" + regionDir.getPath());
3049
3050                Path regioninfoFile =
3051                  new Path(regionDir.getPath(), HRegionFileSystem.REGION_INFO_FILE);
3052                boolean regioninfoFileExists = fs.exists(regioninfoFile);
3053
3054                if (!regioninfoFileExists) {
3055                  // As tables become larger it is more and more likely that by the time you
3056                  // reach a given region that it will be gone due to region splits/merges.
3057                  if (!fs.exists(regionDir.getPath())) {
3058                    LOG.warn("By the time we tried to process this region dir it was already gone: "
3059                      + regionDir.getPath());
3060                    return;
3061                  }
3062                }
3063
3064                HbckRegionInfo hbi = HBaseFsck.this.getOrCreateInfo(encodedName);
3065                HbckRegionInfo.HdfsEntry he = new HbckRegionInfo.HdfsEntry();
3066                synchronized (hbi) {
3067                  if (hbi.getHdfsRegionDir() != null) {
3068                    errors
3069                      .print("Directory " + encodedName + " duplicate??" + hbi.getHdfsRegionDir());
3070                  }
3071
3072                  he.regionDir = regionDir.getPath();
3073                  he.regionDirModTime = regionDir.getModificationTime();
3074                  he.hdfsRegioninfoFilePresent = regioninfoFileExists;
3075                  // we add to orphan list when we attempt to read .regioninfo
3076
3077                  // Set a flag if this region contains only edits
3078                  // This is special case if a region is left after split
3079                  he.hdfsOnlyEdits = true;
3080                  FileStatus[] subDirs = fs.listStatus(regionDir.getPath());
3081                  Path ePath = WALSplitUtil.getRegionDirRecoveredEditsDir(regionDir.getPath());
3082                  for (FileStatus subDir : subDirs) {
3083                    errors.progress();
3084                    String sdName = subDir.getPath().getName();
3085                    if (!sdName.startsWith(".") && !sdName.equals(ePath.getName())) {
3086                      he.hdfsOnlyEdits = false;
3087                      break;
3088                    }
3089                  }
3090                  hbi.setHdfsEntry(he);
3091                }
3092              } catch (Exception e) {
3093                LOG.error("Could not load region dir", e);
3094                exceptions.add(e);
3095              }
3096            }
3097          }));
3098        }
3099
3100        // Ensure all pending tasks are complete (or that we run into an exception)
3101        for (Future<?> f : futures) {
3102          if (!exceptions.isEmpty()) {
3103            break;
3104          }
3105          try {
3106            f.get();
3107          } catch (ExecutionException e) {
3108            LOG.error("Unexpected exec exception!  Should've been caught already.  (Bug?)", e);
3109            // Shouldn't happen, we already logged/caught any exceptions in the Runnable
3110          }
3111        }
3112      } catch (IOException e) {
3113        LOG.error("Cannot execute WorkItemHdfsDir for " + tableDir, e);
3114        exceptions.add(e);
3115      } finally {
3116        if (!exceptions.isEmpty()) {
3117          errors.reportError(ERROR_CODE.RS_CONNECT_FAILURE, "Table Directory: "
3118            + tableDir.getPath().getName() + " Unable to fetch all HDFS region information. ");
3119          // Just throw the first exception as an indication something bad happened
3120          // Don't need to propagate all the exceptions, we already logged them all anyway
3121          throw new ExecutionException("First exception in WorkItemHdfsDir",
3122            exceptions.firstElement());
3123        }
3124      }
3125      return null;
3126    }
3127  }
3128
3129  /**
3130   * Contact hdfs and get all information about specified table directory into regioninfo list.
3131   */
3132  static class WorkItemHdfsRegionInfo implements Callable<Void> {
3133    private HbckRegionInfo hbi;
3134    private HBaseFsck hbck;
3135    private HbckErrorReporter errors;
3136
3137    WorkItemHdfsRegionInfo(HbckRegionInfo hbi, HBaseFsck hbck, HbckErrorReporter errors) {
3138      this.hbi = hbi;
3139      this.hbck = hbck;
3140      this.errors = errors;
3141    }
3142
3143    @Override
3144    public synchronized Void call() throws IOException {
3145      // only load entries that haven't been loaded yet.
3146      if (hbi.getHdfsHRI() == null) {
3147        try {
3148          errors.progress();
3149          hbi.loadHdfsRegioninfo(hbck.getConf());
3150        } catch (IOException ioe) {
3151          String msg = "Orphan region in HDFS: Unable to load .regioninfo from table "
3152            + hbi.getTableName() + " in hdfs dir " + hbi.getHdfsRegionDir()
3153            + "!  It may be an invalid format or version file.  Treating as "
3154            + "an orphaned regiondir.";
3155          errors.reportError(ERROR_CODE.ORPHAN_HDFS_REGION, msg);
3156          try {
3157            hbck.debugLsr(hbi.getHdfsRegionDir());
3158          } catch (IOException ioe2) {
3159            LOG.error("Unable to read directory " + hbi.getHdfsRegionDir(), ioe2);
3160            throw ioe2;
3161          }
3162          hbck.orphanHdfsDirs.add(hbi);
3163          throw ioe;
3164        }
3165      }
3166      return null;
3167    }
3168  }
3169
3170  /**
3171   * Display the full report from fsck. This displays all live and dead region servers, and all
3172   * known regions.
3173   */
3174  public static void setDisplayFullReport() {
3175    details = true;
3176  }
3177
3178  public static boolean shouldDisplayFullReport() {
3179    return details;
3180  }
3181
3182  /**
3183   * Set exclusive mode.
3184   */
3185  public static void setForceExclusive() {
3186    forceExclusive = true;
3187  }
3188
3189  /**
3190   * Only one instance of hbck can modify HBase at a time.
3191   */
3192  public boolean isExclusive() {
3193    return fixAny || forceExclusive;
3194  }
3195
3196  /**
3197   * Set summary mode. Print only summary of the tables and status (OK or INCONSISTENT)
3198   */
3199  static void setSummary() {
3200    summary = true;
3201  }
3202
3203  /**
3204   * Set hbase:meta check mode. Print only info about hbase:meta table deployment/state
3205   */
3206  void setCheckMetaOnly() {
3207    checkMetaOnly = true;
3208  }
3209
3210  /**
3211   * Set region boundaries check mode.
3212   */
3213  void setRegionBoundariesCheck() {
3214    checkRegionBoundaries = true;
3215  }
3216
3217  /**
3218   * Set replication fix mode.
3219   */
3220  public void setFixReplication(boolean shouldFix) {
3221    fixReplication = shouldFix;
3222    fixAny |= shouldFix;
3223  }
3224
3225  public void setCleanReplicationBarrier(boolean shouldClean) {
3226    cleanReplicationBarrier = shouldClean;
3227  }
3228
3229  /**
3230   * Check if we should rerun fsck again. This checks if we've tried to fix something and we should
3231   * rerun fsck tool again. Display the full report from fsck. This displays all live and dead
3232   * region servers, and all known regions.
3233   */
3234  void setShouldRerun() {
3235    rerun = true;
3236  }
3237
3238  public boolean shouldRerun() {
3239    return rerun;
3240  }
3241
3242  /**
3243   * Fix inconsistencies found by fsck. This should try to fix errors (if any) found by fsck
3244   * utility.
3245   */
3246  public void setFixAssignments(boolean shouldFix) {
3247    fixAssignments = shouldFix;
3248    fixAny |= shouldFix;
3249  }
3250
3251  boolean shouldFixAssignments() {
3252    return fixAssignments;
3253  }
3254
3255  public void setFixMeta(boolean shouldFix) {
3256    fixMeta = shouldFix;
3257    fixAny |= shouldFix;
3258  }
3259
3260  boolean shouldFixMeta() {
3261    return fixMeta;
3262  }
3263
3264  public void setFixEmptyMetaCells(boolean shouldFix) {
3265    fixEmptyMetaCells = shouldFix;
3266    fixAny |= shouldFix;
3267  }
3268
3269  boolean shouldFixEmptyMetaCells() {
3270    return fixEmptyMetaCells;
3271  }
3272
3273  public void setCheckHdfs(boolean checking) {
3274    checkHdfs = checking;
3275  }
3276
3277  boolean shouldCheckHdfs() {
3278    return checkHdfs;
3279  }
3280
3281  public void setFixHdfsHoles(boolean shouldFix) {
3282    fixHdfsHoles = shouldFix;
3283    fixAny |= shouldFix;
3284  }
3285
3286  boolean shouldFixHdfsHoles() {
3287    return fixHdfsHoles;
3288  }
3289
3290  public void setFixTableOrphans(boolean shouldFix) {
3291    fixTableOrphans = shouldFix;
3292    fixAny |= shouldFix;
3293  }
3294
3295  boolean shouldFixTableOrphans() {
3296    return fixTableOrphans;
3297  }
3298
3299  public void setFixHdfsOverlaps(boolean shouldFix) {
3300    fixHdfsOverlaps = shouldFix;
3301    fixAny |= shouldFix;
3302  }
3303
3304  boolean shouldFixHdfsOverlaps() {
3305    return fixHdfsOverlaps;
3306  }
3307
3308  public void setFixHdfsOrphans(boolean shouldFix) {
3309    fixHdfsOrphans = shouldFix;
3310    fixAny |= shouldFix;
3311  }
3312
3313  boolean shouldFixHdfsOrphans() {
3314    return fixHdfsOrphans;
3315  }
3316
3317  public void setFixVersionFile(boolean shouldFix) {
3318    fixVersionFile = shouldFix;
3319    fixAny |= shouldFix;
3320  }
3321
3322  public boolean shouldFixVersionFile() {
3323    return fixVersionFile;
3324  }
3325
3326  public void setSidelineBigOverlaps(boolean sbo) {
3327    this.sidelineBigOverlaps = sbo;
3328  }
3329
3330  public boolean shouldSidelineBigOverlaps() {
3331    return sidelineBigOverlaps;
3332  }
3333
3334  public void setFixSplitParents(boolean shouldFix) {
3335    fixSplitParents = shouldFix;
3336    fixAny |= shouldFix;
3337  }
3338
3339  public void setRemoveParents(boolean shouldFix) {
3340    removeParents = shouldFix;
3341    fixAny |= shouldFix;
3342  }
3343
3344  boolean shouldFixSplitParents() {
3345    return fixSplitParents;
3346  }
3347
3348  boolean shouldRemoveParents() {
3349    return removeParents;
3350  }
3351
3352  public void setFixReferenceFiles(boolean shouldFix) {
3353    fixReferenceFiles = shouldFix;
3354    fixAny |= shouldFix;
3355  }
3356
3357  boolean shouldFixReferenceFiles() {
3358    return fixReferenceFiles;
3359  }
3360
3361  public void setFixHFileLinks(boolean shouldFix) {
3362    fixHFileLinks = shouldFix;
3363    fixAny |= shouldFix;
3364  }
3365
3366  boolean shouldFixHFileLinks() {
3367    return fixHFileLinks;
3368  }
3369
3370  public boolean shouldIgnorePreCheckPermission() {
3371    return !fixAny || ignorePreCheckPermission;
3372  }
3373
3374  public void setIgnorePreCheckPermission(boolean ignorePreCheckPermission) {
3375    this.ignorePreCheckPermission = ignorePreCheckPermission;
3376  }
3377
3378  /**
3379   * @param mm maximum number of regions to merge into a single region.
3380   */
3381  public void setMaxMerge(int mm) {
3382    this.maxMerge = mm;
3383  }
3384
3385  public int getMaxMerge() {
3386    return maxMerge;
3387  }
3388
3389  public void setMaxOverlapsToSideline(int mo) {
3390    this.maxOverlapsToSideline = mo;
3391  }
3392
3393  public int getMaxOverlapsToSideline() {
3394    return maxOverlapsToSideline;
3395  }
3396
3397  /**
3398   * Only check/fix tables specified by the list, Empty list means all tables are included.
3399   */
3400  boolean isTableIncluded(TableName table) {
3401    return (tablesIncluded.isEmpty()) || tablesIncluded.contains(table);
3402  }
3403
3404  public void includeTable(TableName table) {
3405    tablesIncluded.add(table);
3406  }
3407
3408  Set<TableName> getIncludedTables() {
3409    return new HashSet<>(tablesIncluded);
3410  }
3411
3412  /**
3413   * We are interested in only those tables that have not changed their state in hbase:meta during
3414   * the last few seconds specified by hbase.admin.fsck.timelag
3415   * @param seconds - the time in seconds
3416   */
3417  public void setTimeLag(long seconds) {
3418    timelag = seconds * 1000; // convert to milliseconds
3419  }
3420
3421  /**
3422   * @param sidelineDir - HDFS path to sideline data
3423   */
3424  public void setSidelineDir(String sidelineDir) {
3425    this.sidelineDir = new Path(sidelineDir);
3426  }
3427
3428  protected HFileCorruptionChecker createHFileCorruptionChecker(boolean sidelineCorruptHFiles)
3429    throws IOException {
3430    return new HFileCorruptionChecker(getConf(), executor, sidelineCorruptHFiles);
3431  }
3432
3433  public HFileCorruptionChecker getHFilecorruptionChecker() {
3434    return hfcc;
3435  }
3436
3437  public void setHFileCorruptionChecker(HFileCorruptionChecker hfcc) {
3438    this.hfcc = hfcc;
3439  }
3440
3441  public void setRetCode(int code) {
3442    this.retcode = code;
3443  }
3444
3445  public int getRetCode() {
3446    return retcode;
3447  }
3448
3449  protected HBaseFsck printUsageAndExit() {
3450    StringWriter sw = new StringWriter(2048);
3451    PrintWriter out = new PrintWriter(sw);
3452    out.println("");
3453    out.println("-----------------------------------------------------------------------");
3454    out.println("NOTE: As of HBase version 2.0, the hbck tool is significantly changed.");
3455    out.println("In general, all Read-Only options are supported and can be be used");
3456    out.println("safely. Most -fix/ -repair options are NOT supported. Please see usage");
3457    out.println("below for details on which options are not supported.");
3458    out.println("-----------------------------------------------------------------------");
3459    out.println("");
3460    out.println("Usage: fsck [opts] {only tables}");
3461    out.println(" where [opts] are:");
3462    out.println("   -help Display help options (this)");
3463    out.println("   -details Display full report of all regions.");
3464    out.println("   -timelag <timeInSeconds>  Process only regions that "
3465      + " have not experienced any metadata updates in the last " + " <timeInSeconds> seconds.");
3466    out.println("   -sleepBeforeRerun <timeInSeconds> Sleep this many seconds"
3467      + " before checking if the fix worked if run with -fix");
3468    out.println("   -summary Print only summary of the tables and status.");
3469    out.println("   -metaonly Only check the state of the hbase:meta table.");
3470    out.println("   -sidelineDir <hdfs://> HDFS path to backup existing meta.");
3471    out.println(
3472      "   -boundaries Verify that regions boundaries are the same between META and store files.");
3473    out.println("   -exclusive Abort if another hbck is exclusive or fixing.");
3474
3475    out.println("");
3476    out.println("  Datafile Repair options: (expert features, use with caution!)");
3477    out.println(
3478      "   -checkCorruptHFiles     Check all Hfiles by opening them to make sure they are valid");
3479    out.println(
3480      "   -sidelineCorruptHFiles  Quarantine corrupted HFiles.  implies -checkCorruptHFiles");
3481
3482    out.println("");
3483    out.println(" Replication options");
3484    out.println("   -fixReplication   Deletes replication queues for removed peers");
3485
3486    out.println("");
3487    out.println(
3488      "  Metadata Repair options supported as of version 2.0: (expert features, use with caution!)");
3489    out.println("   -fixVersionFile   Try to fix missing hbase.version file in hdfs.");
3490    out.println("   -fixReferenceFiles  Try to offline lingering reference store files");
3491    out.println("   -fixHFileLinks  Try to offline lingering HFileLinks");
3492    out.println("   -noHdfsChecking   Don't load/check region info from HDFS."
3493      + " Assumes hbase:meta region info is good. Won't check/fix any HDFS issue, e.g. hole, orphan, or overlap");
3494    out.println("   -ignorePreCheckPermission  ignore filesystem permission pre-check");
3495
3496    out.println("");
3497    out.println("NOTE: Following options are NOT supported as of HBase version 2.0+.");
3498    out.println("");
3499    out.println("  UNSUPPORTED Metadata Repair options: (expert features, use with caution!)");
3500    out.println(
3501      "   -fix              Try to fix region assignments.  This is for backwards compatibility");
3502    out.println("   -fixAssignments   Try to fix region assignments.  Replaces the old -fix");
3503    out.println(
3504      "   -fixMeta          Try to fix meta problems.  This assumes HDFS region info is good.");
3505    out.println("   -fixHdfsHoles     Try to fix region holes in hdfs.");
3506    out.println("   -fixHdfsOrphans   Try to fix region dirs with no .regioninfo file in hdfs");
3507    out.println(
3508      "   -fixTableOrphans  Try to fix table dirs with no .tableinfo file in hdfs (online mode only)");
3509    out.println("   -fixHdfsOverlaps  Try to fix region overlaps in hdfs.");
3510    out.println(
3511      "   -maxMerge <n>     When fixing region overlaps, allow at most <n> regions to merge. (n="
3512        + DEFAULT_MAX_MERGE + " by default)");
3513    out.println(
3514      "   -sidelineBigOverlaps  When fixing region overlaps, allow to sideline big overlaps");
3515    out.println(
3516      "   -maxOverlapsToSideline <n>  When fixing region overlaps, allow at most <n> regions to sideline per group. (n="
3517        + DEFAULT_OVERLAPS_TO_SIDELINE + " by default)");
3518    out.println("   -fixSplitParents  Try to force offline split parents to be online.");
3519    out.println(
3520      "   -removeParents    Try to offline and sideline lingering parents and keep daughter regions.");
3521    out.println("   -fixEmptyMetaCells  Try to fix hbase:meta entries not referencing any region"
3522      + " (empty REGIONINFO_QUALIFIER rows)");
3523
3524    out.println("");
3525    out.println("  UNSUPPORTED Metadata Repair shortcuts");
3526    out.println("   -repair           Shortcut for -fixAssignments -fixMeta -fixHdfsHoles "
3527      + "-fixHdfsOrphans -fixHdfsOverlaps -fixVersionFile -sidelineBigOverlaps -fixReferenceFiles"
3528      + "-fixHFileLinks");
3529    out.println("   -repairHoles      Shortcut for -fixAssignments -fixMeta -fixHdfsHoles");
3530    out.println("");
3531    out.println(" Replication options");
3532    out.println("   -fixReplication   Deletes replication queues for removed peers");
3533    out.println("   -cleanReplicationBarrier [tableName] clean the replication barriers "
3534      + "of a specified table, tableName is required");
3535    out.flush();
3536    errors.reportError(ERROR_CODE.WRONG_USAGE, sw.toString());
3537
3538    setRetCode(-2);
3539    return this;
3540  }
3541
3542  /**
3543   * Main program
3544   */
3545  public static void main(String[] args) throws Exception {
3546    // create a fsck object
3547    Configuration conf = HBaseConfiguration.create();
3548    Path hbasedir = CommonFSUtils.getRootDir(conf);
3549    URI defaultFs = hbasedir.getFileSystem(conf).getUri();
3550    CommonFSUtils.setFsDefault(conf, new Path(defaultFs));
3551    int ret = ToolRunner.run(new HBaseFsckTool(conf), args);
3552    System.exit(ret);
3553  }
3554
3555  /**
3556   * This is a Tool wrapper that gathers -Dxxx=yyy configuration settings from the command line.
3557   */
3558  static class HBaseFsckTool extends Configured implements Tool {
3559    HBaseFsckTool(Configuration conf) {
3560      super(conf);
3561    }
3562
3563    @Override
3564    public int run(String[] args) throws Exception {
3565      HBaseFsck hbck = new HBaseFsck(getConf());
3566      hbck.exec(hbck.executor, args);
3567      hbck.close();
3568      return hbck.getRetCode();
3569    }
3570  }
3571
3572  public HBaseFsck exec(ExecutorService exec, String[] args)
3573    throws KeeperException, IOException, InterruptedException, ReplicationException {
3574    long sleepBeforeRerun = DEFAULT_SLEEP_BEFORE_RERUN;
3575
3576    boolean checkCorruptHFiles = false;
3577    boolean sidelineCorruptHFiles = false;
3578
3579    // Process command-line args.
3580    for (int i = 0; i < args.length; i++) {
3581      String cmd = args[i];
3582      if (cmd.equals("-help") || cmd.equals("-h")) {
3583        return printUsageAndExit();
3584      } else if (cmd.equals("-details")) {
3585        setDisplayFullReport();
3586      } else if (cmd.equals("-exclusive")) {
3587        setForceExclusive();
3588      } else if (cmd.equals("-timelag")) {
3589        if (i == args.length - 1) {
3590          errors.reportError(ERROR_CODE.WRONG_USAGE, "HBaseFsck: -timelag needs a value.");
3591          return printUsageAndExit();
3592        }
3593        try {
3594          long timelag = Long.parseLong(args[++i]);
3595          setTimeLag(timelag);
3596        } catch (NumberFormatException e) {
3597          errors.reportError(ERROR_CODE.WRONG_USAGE, "-timelag needs a numeric value.");
3598          return printUsageAndExit();
3599        }
3600      } else if (cmd.equals("-sleepBeforeRerun")) {
3601        if (i == args.length - 1) {
3602          errors.reportError(ERROR_CODE.WRONG_USAGE, "HBaseFsck: -sleepBeforeRerun needs a value.");
3603          return printUsageAndExit();
3604        }
3605        try {
3606          sleepBeforeRerun = Long.parseLong(args[++i]);
3607        } catch (NumberFormatException e) {
3608          errors.reportError(ERROR_CODE.WRONG_USAGE, "-sleepBeforeRerun needs a numeric value.");
3609          return printUsageAndExit();
3610        }
3611      } else if (cmd.equals("-sidelineDir")) {
3612        if (i == args.length - 1) {
3613          errors.reportError(ERROR_CODE.WRONG_USAGE, "HBaseFsck: -sidelineDir needs a value.");
3614          return printUsageAndExit();
3615        }
3616        setSidelineDir(args[++i]);
3617      } else if (cmd.equals("-fix")) {
3618        errors.reportError(ERROR_CODE.WRONG_USAGE,
3619          "This option is deprecated, please use  -fixAssignments instead.");
3620        setFixAssignments(true);
3621      } else if (cmd.equals("-fixAssignments")) {
3622        setFixAssignments(true);
3623      } else if (cmd.equals("-fixMeta")) {
3624        setFixMeta(true);
3625      } else if (cmd.equals("-noHdfsChecking")) {
3626        setCheckHdfs(false);
3627      } else if (cmd.equals("-fixHdfsHoles")) {
3628        setFixHdfsHoles(true);
3629      } else if (cmd.equals("-fixHdfsOrphans")) {
3630        setFixHdfsOrphans(true);
3631      } else if (cmd.equals("-fixTableOrphans")) {
3632        setFixTableOrphans(true);
3633      } else if (cmd.equals("-fixHdfsOverlaps")) {
3634        setFixHdfsOverlaps(true);
3635      } else if (cmd.equals("-fixVersionFile")) {
3636        setFixVersionFile(true);
3637      } else if (cmd.equals("-sidelineBigOverlaps")) {
3638        setSidelineBigOverlaps(true);
3639      } else if (cmd.equals("-fixSplitParents")) {
3640        setFixSplitParents(true);
3641      } else if (cmd.equals("-removeParents")) {
3642        setRemoveParents(true);
3643      } else if (cmd.equals("-ignorePreCheckPermission")) {
3644        setIgnorePreCheckPermission(true);
3645      } else if (cmd.equals("-checkCorruptHFiles")) {
3646        checkCorruptHFiles = true;
3647      } else if (cmd.equals("-sidelineCorruptHFiles")) {
3648        sidelineCorruptHFiles = true;
3649      } else if (cmd.equals("-fixReferenceFiles")) {
3650        setFixReferenceFiles(true);
3651      } else if (cmd.equals("-fixHFileLinks")) {
3652        setFixHFileLinks(true);
3653      } else if (cmd.equals("-fixEmptyMetaCells")) {
3654        setFixEmptyMetaCells(true);
3655      } else if (cmd.equals("-repair")) {
3656        // this attempts to merge overlapping hdfs regions, needs testing
3657        // under load
3658        setFixHdfsHoles(true);
3659        setFixHdfsOrphans(true);
3660        setFixMeta(true);
3661        setFixAssignments(true);
3662        setFixHdfsOverlaps(true);
3663        setFixVersionFile(true);
3664        setSidelineBigOverlaps(true);
3665        setFixSplitParents(false);
3666        setCheckHdfs(true);
3667        setFixReferenceFiles(true);
3668        setFixHFileLinks(true);
3669      } else if (cmd.equals("-repairHoles")) {
3670        // this will make all missing hdfs regions available but may lose data
3671        setFixHdfsHoles(true);
3672        setFixHdfsOrphans(false);
3673        setFixMeta(true);
3674        setFixAssignments(true);
3675        setFixHdfsOverlaps(false);
3676        setSidelineBigOverlaps(false);
3677        setFixSplitParents(false);
3678        setCheckHdfs(true);
3679      } else if (cmd.equals("-maxOverlapsToSideline")) {
3680        if (i == args.length - 1) {
3681          errors.reportError(ERROR_CODE.WRONG_USAGE,
3682            "-maxOverlapsToSideline needs a numeric value argument.");
3683          return printUsageAndExit();
3684        }
3685        try {
3686          int maxOverlapsToSideline = Integer.parseInt(args[++i]);
3687          setMaxOverlapsToSideline(maxOverlapsToSideline);
3688        } catch (NumberFormatException e) {
3689          errors.reportError(ERROR_CODE.WRONG_USAGE,
3690            "-maxOverlapsToSideline needs a numeric value argument.");
3691          return printUsageAndExit();
3692        }
3693      } else if (cmd.equals("-maxMerge")) {
3694        if (i == args.length - 1) {
3695          errors.reportError(ERROR_CODE.WRONG_USAGE, "-maxMerge needs a numeric value argument.");
3696          return printUsageAndExit();
3697        }
3698        try {
3699          int maxMerge = Integer.parseInt(args[++i]);
3700          setMaxMerge(maxMerge);
3701        } catch (NumberFormatException e) {
3702          errors.reportError(ERROR_CODE.WRONG_USAGE, "-maxMerge needs a numeric value argument.");
3703          return printUsageAndExit();
3704        }
3705      } else if (cmd.equals("-summary")) {
3706        setSummary();
3707      } else if (cmd.equals("-metaonly")) {
3708        setCheckMetaOnly();
3709      } else if (cmd.equals("-boundaries")) {
3710        setRegionBoundariesCheck();
3711      } else if (cmd.equals("-fixReplication")) {
3712        setFixReplication(true);
3713      } else if (cmd.equals("-cleanReplicationBarrier")) {
3714        setCleanReplicationBarrier(true);
3715        if (args[++i].startsWith("-")) {
3716          printUsageAndExit();
3717        }
3718        setCleanReplicationBarrierTable(args[i]);
3719      } else if (cmd.startsWith("-")) {
3720        errors.reportError(ERROR_CODE.WRONG_USAGE, "Unrecognized option:" + cmd);
3721        return printUsageAndExit();
3722      } else {
3723        includeTable(TableName.valueOf(cmd));
3724        errors.print("Allow checking/fixes for table: " + cmd);
3725      }
3726    }
3727
3728    errors.print("HBaseFsck command line options: " + StringUtils.join(args, " "));
3729
3730    // pre-check current user has FS write permission or not
3731    try {
3732      preCheckPermission();
3733    } catch (IOException ioe) {
3734      Runtime.getRuntime().exit(-1);
3735    }
3736
3737    // do the real work of hbck
3738    connect();
3739
3740    // after connecting to server above, we have server version
3741    // check if unsupported option is specified based on server version
3742    if (!isOptionsSupported(args)) {
3743      return printUsageAndExit();
3744    }
3745
3746    try {
3747      // if corrupt file mode is on, first fix them since they may be opened later
3748      if (checkCorruptHFiles || sidelineCorruptHFiles) {
3749        LOG.info("Checking all hfiles for corruption");
3750        HFileCorruptionChecker hfcc = createHFileCorruptionChecker(sidelineCorruptHFiles);
3751        setHFileCorruptionChecker(hfcc); // so we can get result
3752        Collection<TableName> tables = getIncludedTables();
3753        Collection<Path> tableDirs = new ArrayList<>();
3754        Path rootdir = CommonFSUtils.getRootDir(getConf());
3755        if (tables.size() > 0) {
3756          for (TableName t : tables) {
3757            tableDirs.add(CommonFSUtils.getTableDir(rootdir, t));
3758          }
3759        } else {
3760          tableDirs = FSUtils.getTableDirs(CommonFSUtils.getCurrentFileSystem(getConf()), rootdir);
3761        }
3762        hfcc.checkTables(tableDirs);
3763        hfcc.report(errors);
3764      }
3765
3766      // check and fix table integrity, region consistency.
3767      int code = onlineHbck();
3768      setRetCode(code);
3769      // If we have changed the HBase state it is better to run hbck again
3770      // to see if we haven't broken something else in the process.
3771      // We run it only once more because otherwise we can easily fall into
3772      // an infinite loop.
3773      if (shouldRerun()) {
3774        try {
3775          LOG.info("Sleeping " + sleepBeforeRerun + "ms before re-checking after fix...");
3776          Thread.sleep(sleepBeforeRerun);
3777        } catch (InterruptedException ie) {
3778          LOG.warn("Interrupted while sleeping");
3779          return this;
3780        }
3781        // Just report
3782        setFixAssignments(false);
3783        setFixMeta(false);
3784        setFixHdfsHoles(false);
3785        setFixHdfsOverlaps(false);
3786        setFixVersionFile(false);
3787        setFixTableOrphans(false);
3788        errors.resetErrors();
3789        code = onlineHbck();
3790        setRetCode(code);
3791      }
3792    } finally {
3793      IOUtils.closeQuietly(this, e -> LOG.warn("", e));
3794    }
3795    return this;
3796  }
3797
3798  private boolean isOptionsSupported(String[] args) {
3799    boolean result = true;
3800    String hbaseServerVersion = status.getHBaseVersion();
3801    if (VersionInfo.compareVersion("2.any.any", hbaseServerVersion) < 0) {
3802      // Process command-line args.
3803      for (String arg : args) {
3804        if (unsupportedOptionsInV2.contains(arg)) {
3805          errors.reportError(ERROR_CODE.UNSUPPORTED_OPTION,
3806            "option '" + arg + "' is not " + "supported!");
3807          result = false;
3808          break;
3809        }
3810      }
3811    }
3812    return result;
3813  }
3814
3815  public void setCleanReplicationBarrierTable(String cleanReplicationBarrierTable) {
3816    this.cleanReplicationBarrierTable = TableName.valueOf(cleanReplicationBarrierTable);
3817  }
3818
3819  public void cleanReplicationBarrier() throws IOException {
3820    if (!cleanReplicationBarrier || cleanReplicationBarrierTable == null) {
3821      return;
3822    }
3823    if (cleanReplicationBarrierTable.isSystemTable()) {
3824      errors.reportError(ERROR_CODE.INVALID_TABLE,
3825        "invalid table: " + cleanReplicationBarrierTable);
3826      return;
3827    }
3828
3829    boolean isGlobalScope = false;
3830    try {
3831      isGlobalScope = admin.getDescriptor(cleanReplicationBarrierTable).hasGlobalReplicationScope();
3832    } catch (TableNotFoundException e) {
3833      LOG.info("we may need to clean some erroneous data due to bugs");
3834    }
3835
3836    if (isGlobalScope) {
3837      errors.reportError(ERROR_CODE.INVALID_TABLE,
3838        "table's replication scope is global: " + cleanReplicationBarrierTable);
3839      return;
3840    }
3841    List<byte[]> regionNames = new ArrayList<>();
3842    Scan barrierScan = new Scan();
3843    barrierScan.setCaching(100);
3844    barrierScan.addFamily(HConstants.REPLICATION_BARRIER_FAMILY);
3845    barrierScan
3846      .withStartRow(ClientMetaTableAccessor.getTableStartRowForMeta(cleanReplicationBarrierTable,
3847        ClientMetaTableAccessor.QueryType.REGION))
3848      .withStopRow(ClientMetaTableAccessor.getTableStopRowForMeta(cleanReplicationBarrierTable,
3849        ClientMetaTableAccessor.QueryType.REGION));
3850    Result result;
3851    try (ResultScanner scanner = meta.getScanner(barrierScan)) {
3852      while ((result = scanner.next()) != null) {
3853        regionNames.add(result.getRow());
3854      }
3855    }
3856    if (regionNames.size() <= 0) {
3857      errors.reportError(ERROR_CODE.INVALID_TABLE,
3858        "there is no barriers of this table: " + cleanReplicationBarrierTable);
3859      return;
3860    }
3861    ReplicationQueueStorage queueStorage =
3862      ReplicationStorageFactory.getReplicationQueueStorage(connection, getConf());
3863    List<ReplicationPeerDescription> peerDescriptions = admin.listReplicationPeers();
3864    if (peerDescriptions != null && peerDescriptions.size() > 0) {
3865      List<String> peers = peerDescriptions.stream()
3866        .filter(
3867          peerConfig -> peerConfig.getPeerConfig().needToReplicate(cleanReplicationBarrierTable))
3868        .map(peerConfig -> peerConfig.getPeerId()).collect(Collectors.toList());
3869      try {
3870        List<String> batch = new ArrayList<>();
3871        for (String peer : peers) {
3872          for (byte[] regionName : regionNames) {
3873            batch.add(RegionInfo.encodeRegionName(regionName));
3874            if (batch.size() % 100 == 0) {
3875              queueStorage.removeLastSequenceIds(peer, batch);
3876              batch.clear();
3877            }
3878          }
3879          if (batch.size() > 0) {
3880            queueStorage.removeLastSequenceIds(peer, batch);
3881            batch.clear();
3882          }
3883        }
3884      } catch (ReplicationException re) {
3885        throw new IOException(re);
3886      }
3887    }
3888    for (byte[] regionName : regionNames) {
3889      meta.delete(new Delete(regionName).addFamily(HConstants.REPLICATION_BARRIER_FAMILY));
3890    }
3891    setShouldRerun();
3892  }
3893
3894  /**
3895   * ls -r for debugging purposes
3896   */
3897  void debugLsr(Path p) throws IOException {
3898    debugLsr(getConf(), p, errors);
3899  }
3900
3901  /**
3902   * ls -r for debugging purposes
3903   */
3904  public static void debugLsr(Configuration conf, Path p) throws IOException {
3905    debugLsr(conf, p, new PrintingErrorReporter());
3906  }
3907
3908  /**
3909   * ls -r for debugging purposes
3910   */
3911  public static void debugLsr(Configuration conf, Path p, HbckErrorReporter errors)
3912    throws IOException {
3913    if (!LOG.isDebugEnabled() || p == null) {
3914      return;
3915    }
3916    FileSystem fs = p.getFileSystem(conf);
3917
3918    if (!fs.exists(p)) {
3919      // nothing
3920      return;
3921    }
3922    errors.print(p.toString());
3923
3924    if (fs.isFile(p)) {
3925      return;
3926    }
3927
3928    if (fs.getFileStatus(p).isDirectory()) {
3929      FileStatus[] fss = fs.listStatus(p);
3930      for (FileStatus status : fss) {
3931        debugLsr(conf, status.getPath(), errors);
3932      }
3933    }
3934  }
3935}