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