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