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.tool;
019
020import java.io.IOException;
021import java.util.Collection;
022import java.util.List;
023import java.util.concurrent.ExecutorService;
024import java.util.concurrent.Executors;
025import java.util.concurrent.TimeUnit;
026import org.apache.hadoop.conf.Configuration;
027import org.apache.hadoop.fs.FileSystem;
028import org.apache.hadoop.fs.Path;
029import org.apache.hadoop.hbase.HBaseInterfaceAudience;
030import org.apache.hadoop.hbase.HConstants;
031import org.apache.hadoop.hbase.util.AbstractHBaseTool;
032import org.apache.hadoop.hbase.util.CommonFSUtils;
033import org.apache.hadoop.hbase.util.FSUtils;
034import org.apache.hadoop.hbase.util.Threads;
035import org.apache.hadoop.hbase.util.hbck.HFileCorruptionChecker;
036import org.apache.yetus.audience.InterfaceAudience;
037import org.slf4j.Logger;
038import org.slf4j.LoggerFactory;
039
040import org.apache.hbase.thirdparty.com.google.common.util.concurrent.ThreadFactoryBuilder;
041import org.apache.hbase.thirdparty.org.apache.commons.cli.CommandLine;
042
043@InterfaceAudience.LimitedPrivate(HBaseInterfaceAudience.TOOLS)
044public class HFileContentValidator extends AbstractHBaseTool {
045
046  private static final Logger LOG = LoggerFactory.getLogger(HFileContentValidator.class);
047
048  /**
049   * Check HFile contents are readable by HBase 2.
050   * @param conf used configuration
051   * @return number of HFiles corrupted HBase
052   * @throws IOException if a remote or network exception occurs
053   */
054  private boolean validateHFileContent(Configuration conf) throws IOException {
055    FileSystem fileSystem = CommonFSUtils.getCurrentFileSystem(conf);
056
057    ExecutorService threadPool = createThreadPool(conf);
058    HFileCorruptionChecker checker;
059
060    try {
061      checker = new HFileCorruptionChecker(conf, threadPool, false);
062
063      Path rootDir = CommonFSUtils.getRootDir(conf);
064      LOG.info("Validating HFile contents under {}", rootDir);
065
066      Collection<Path> tableDirs = FSUtils.getTableDirs(fileSystem, rootDir);
067      checker.checkTables(tableDirs);
068
069      Path archiveRootDir = new Path(rootDir, HConstants.HFILE_ARCHIVE_DIRECTORY);
070      LOG.info("Validating HFile contents under {}", archiveRootDir);
071
072      List<Path> archiveTableDirs = FSUtils.getTableDirs(fileSystem, archiveRootDir);
073      checker.checkTables(archiveTableDirs);
074    } finally {
075      threadPool.shutdown();
076
077      try {
078        threadPool.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS);
079      } catch (InterruptedException e) {
080        Thread.currentThread().interrupt();
081      }
082    }
083
084    int checkedFiles = checker.getHFilesChecked();
085    Collection<Path> corrupted = checker.getCorrupted();
086
087    if (corrupted.isEmpty()) {
088      LOG.info("Checked {} HFiles, none of them are corrupted.", checkedFiles);
089      LOG.info("There are no incompatible HFiles.");
090
091      return true;
092    } else {
093      LOG.info("Checked {} HFiles, {} are corrupted.", checkedFiles, corrupted.size());
094
095      for (Path path : corrupted) {
096        LOG.info("Corrupted file: {}", path);
097      }
098
099      LOG.info("Change data block encodings before upgrading. "
100        + "Check https://s.apache.org/prefixtree for instructions.");
101
102      return false;
103    }
104  }
105
106  private ExecutorService createThreadPool(Configuration conf) {
107    int availableProcessors = Runtime.getRuntime().availableProcessors();
108    int numThreads = conf.getInt("hfilevalidator.numthreads", availableProcessors);
109    return Executors.newFixedThreadPool(numThreads,
110      new ThreadFactoryBuilder().setNameFormat("hfile-validator-pool-%d").setDaemon(true)
111        .setUncaughtExceptionHandler(Threads.LOGGING_EXCEPTION_HANDLER).build());
112  }
113
114  @Override
115  protected void addOptions() {
116  }
117
118  @Override
119  protected void processOptions(CommandLine cmd) {
120  }
121
122  @Override
123  protected int doWork() throws Exception {
124    return (validateHFileContent(getConf())) ? EXIT_SUCCESS : EXIT_FAILURE;
125  }
126}